feat(studio): highlight timeline selection sets

Render selected styling from selectedElementIds in the timeline.

Sync the set into preview group selection boxes without collapsing the anchor.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-09 16:54:34 -04:00
parent cca31feffe
commit 1d858f004d
8 changed files with 363 additions and 4 deletions
@@ -203,6 +203,40 @@ describe("Timeline provider boundary", () => {
expect(onSeek).not.toHaveBeenCalled();
act(() => root.unmount());
});
it("marks every clip in selectedElementIds as selected", () => {
const host = document.createElement("div");
document.body.append(host);
Object.defineProperty(host, "clientWidth", {
configurable: true,
value: 720,
});
usePlayerStore.setState({
duration: 6,
timelineReady: true,
selectedElementId: "clip-2",
selectedElementIds: new Set(["clip-1", "clip-2"]),
elements: [
{ id: "clip-1", tag: "div", start: 0, duration: 1, track: 0 },
{ id: "clip-2", tag: "div", start: 1.5, duration: 1, track: 1 },
{ id: "clip-3", tag: "div", start: 3, duration: 1, track: 2 },
],
});
const root = createRoot(host);
act(() => {
root.render(React.createElement(Timeline));
});
const selectedClips = host.querySelectorAll(".timeline-clip.is-selected");
expect(selectedClips).toHaveLength(2);
expect(host.querySelector('[data-el-id="clip-3"]')?.classList.contains("is-selected")).toBe(
false,
);
act(() => root.unmount());
});
});
describe("generateTicks", () => {
@@ -158,6 +158,7 @@ export const TimelineCanvas = memo(function TimelineCanvas({
onRazorSplitAll,
} = useTimelineEditContextOptional();
const beatDragging = usePlayerStore((s) => s.beatDragging);
const selectedElementIds = usePlayerStore((s) => s.selectedElementIds);
const activeSnapGuideTime = draggedClip?.started
? (draggedClip.snapBeatTime ?? draggedClip.snapGuideTime)
: resizingClip?.started
@@ -359,7 +360,7 @@ export const TimelineCanvas = memo(function TimelineCanvas({
const clipStyle = getTrackStyle(el.tag);
const elementKey = el.key ?? el.id;
const capabilities = getTimelineEditCapabilities(el);
const isSelected = selectedElementId === elementKey;
const isSelected = selectedElementIds.has(elementKey);
const isComposition = !!el.compositionSrc;
// elementKey (el.key ?? el.id) is already unique per clip; do NOT
// fold in the map index, or a splice/reorder remounts every clip
@@ -102,4 +102,15 @@ describe("TimelineClip", () => {
act(() => root.unmount());
});
it("applies selected styling when rendered as selected", () => {
const { host, root } = renderClip({
element: { id: "selected", label: "Selected", tag: "div", start: 0, duration: 1, track: 0 },
isSelected: true,
});
expect(host.querySelector(".timeline-clip")?.classList.contains("is-selected")).toBe(true);
act(() => root.unmount());
});
});