Files
hyperframes/packages/studio/src/player/components/timelineDropIndicator.ts
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

34 lines
1.2 KiB
TypeScript

import type { TimelineLayerDropPlacement } from "./timelineStacking";
import type { TimelineLayerId } from "./timelineTrackOrder";
export type TimelineDropIndicator = { kind: "onto" } | { kind: "line"; edge: "top" | "bottom" };
interface ResolveTimelineDropIndicatorInput {
placement: TimelineLayerDropPlacement | null;
layerId: TimelineLayerId;
layerOrder: readonly TimelineLayerId[];
}
export function resolveTimelineDropIndicator({
placement,
layerId,
layerOrder,
}: ResolveTimelineDropIndicatorInput): TimelineDropIndicator | null {
if (!placement || !layerOrder.includes(layerId)) return null;
switch (placement.type) {
case "onto":
return placement.layerId === layerId ? { kind: "onto" } : null;
case "between":
if (placement.beforeLayerId === layerId) return { kind: "line", edge: "bottom" };
if (!layerOrder.includes(placement.beforeLayerId) && placement.afterLayerId === layerId) {
return { kind: "line", edge: "top" };
}
return null;
case "above":
return placement.layerId === layerId ? { kind: "line", edge: "top" } : null;
case "below":
return placement.layerId === layerId ? { kind: "line", edge: "bottom" } : null;
}
}