Files
hyperframes/packages/studio/src/player/components/TimelineDragGhost.tsx
T
Miguel Angel Simon Sierra bc5fc410ee feat(studio): timeline layer UI — context headers, audio lanes, drop affordances
L4 polish for the stacking-layer timeline:
- Sub-composition layers group under a slim "Inside: <comp>" header with a green
  accent, so it reads that restacking there is scoped to that context.
- Audio clips render in a distinct bottom lane: separator border, muted row bg,
  music glyph in the gutter, beat strip — clearly not part of the z-order stack.
- During a vertical drag, a drop affordance shows join-vs-insert: an "onto"
  target highlights the row (join that layer / same z); "between"/"above"/
  "below" shows an insertion line (new layer here).

New pure helper timelineDropIndicator.ts (+ test) maps placement -> indicator;
row-group rendering extracted into TimelineLayerGroupHeader / TimelineLayerGutter
/ TimelineDropInsertionLine / TimelineDragGhost to keep files under the 600 cap.
2026-07-08 23:46:26 -04:00

60 lines
1.6 KiB
TypeScript

import type { ReactNode } from "react";
import { TimelineClip } from "./TimelineClip";
import { getTimelineEditCapabilities } from "./timelineEditing";
import { CLIP_Y, TRACK_H } from "./timelineLayout";
import type { TimelineTheme } from "./timelineTheme";
import type { TimelineElement } from "../store/playerStore";
interface TimelineDragGhostProps {
element: TimelineElement;
position: { left: number; top: number };
pps: number;
selectedElementId: string | null;
hasCustomContent: boolean;
theme: TimelineTheme;
children: ReactNode;
}
export function TimelineDragGhost({
element,
position,
pps,
selectedElementId,
hasCustomContent,
theme,
children,
}: TimelineDragGhostProps) {
return (
<div
className="absolute pointer-events-none"
style={{
top: position.top,
left: position.left,
width: Math.max(element.duration * pps, 4),
height: TRACK_H - CLIP_Y * 2,
zIndex: 40,
}}
>
<TimelineClip
el={{ ...element, start: 0 }}
pps={pps}
clipY={0}
isSelected={selectedElementId === (element.key ?? element.id)}
isHovered={false}
isDragging={true}
hasCustomContent={hasCustomContent}
capabilities={getTimelineEditCapabilities(element)}
theme={theme}
isComposition={!!element.compositionSrc}
onHoverStart={() => {}}
onHoverEnd={() => {}}
onResizeStart={() => {}}
onClick={() => {}}
onDoubleClick={() => {}}
>
{children}
</TimelineClip>
</div>
);
}