refactor: rename RowWaveform to BackgroundWaveform, drop wrapper div

Rename to BackgroundWaveform to reflect its visual role rather than
structural placement. Remove the redundant wrapper div — Row already
provides relative overflow-hidden, so the canvas can sit absolute
inset-0 directly and observe itself via ResizeObserver.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Davide Mendolia
2026-05-23 18:06:28 +02:00
co-authored by Claude Sonnet 4.6
parent b7d7c08906
commit 98f7441dfa
2 changed files with 15 additions and 21 deletions
@@ -1,39 +1,40 @@
import { useTimelineContext } from "dnd-timeline";
import { useEffect, useRef, useState } from "react";
export interface RowWaveformProps {
export interface BackgroundWaveformProps {
/** Pre-computed peaks array: pairs of [min, max] per block (length = 2 * N). */
peaks: Float32Array | null;
videoDurationMs: number;
}
/**
* Renders a faint audio waveform on a `<canvas>` element that fills its
* containing row. Designed to be passed as the `background` prop of `<Row>`.
* Renders a faint audio waveform on a `<canvas>` that fills its containing
* block. Designed to be passed as the `background` prop of `<Row>`, which
* already provides `relative overflow-hidden` — no wrapper element needed.
*
* - Accepts pre-computed `peaks` from the caller (see `useAudioPeaks`).
* - Redraws whenever the timeline zoom/pan range changes.
* - `pointer-events: none` throughout — never blocks drag-to-create interactions.
* - `pointer-events: none` — never blocks drag-to-create interactions.
*/
export default function RowWaveform({ peaks, videoDurationMs }: RowWaveformProps) {
export default function BackgroundWaveform({ peaks, videoDurationMs }: BackgroundWaveformProps) {
const { range } = useTimelineContext();
const canvasRef = useRef<HTMLCanvasElement>(null);
const wrapperRef = useRef<HTMLDivElement>(null);
const [canvasSize, setCanvasSize] = useState({ w: 0, h: 0 });
// Track container dimensions via ResizeObserver.
// Observe the canvas itself — Row's `relative overflow-hidden` parent
// makes it fill the row exactly, so no wrapper div is needed.
useEffect(() => {
const el = wrapperRef.current;
if (!el) return;
const canvas = canvasRef.current;
if (!canvas) return;
const ro = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
setCanvasSize({ w: width, h: height });
});
ro.observe(el);
ro.observe(canvas);
return () => ro.disconnect();
}, []);
// Redraw whenever peaks, range, or container size changes.
// Redraw whenever peaks, range, or canvas size changes.
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas || !peaks || canvasSize.w <= 0 || canvasSize.h <= 0) return;
@@ -83,12 +84,5 @@ export default function RowWaveform({ peaks, videoDurationMs }: RowWaveformProps
ctx.stroke();
}, [peaks, range, canvasSize, videoDurationMs]);
return (
<div ref={wrapperRef} className="absolute inset-0 pointer-events-none overflow-hidden">
<canvas
ref={canvasRef}
style={{ width: canvasSize.w, height: canvasSize.h, display: "block" }}
/>
</div>
);
return <canvas ref={canvasRef} className="absolute inset-0 pointer-events-none w-full h-full" />;
}
@@ -35,10 +35,10 @@ import type {
ZoomFocus,
ZoomRegion,
} from "../types";
import BackgroundWaveform from "./BackgroundWaveform";
import Item from "./Item";
import KeyframeMarkers from "./KeyframeMarkers";
import Row from "./Row";
import RowWaveform from "./RowWaveform";
import TimelineWrapper from "./TimelineWrapper";
import { detectZoomDwellCandidates, normalizeCursorTelemetry } from "./zoomSuggestionUtils";
@@ -803,7 +803,7 @@ function Timeline({
hint={t("hints.pressTrim")}
background={
showTrimWaveform ? (
<RowWaveform peaks={peaks} videoDurationMs={videoDurationMs} />
<BackgroundWaveform peaks={peaks} videoDurationMs={videoDurationMs} />
) : undefined
}
>