fix(studio): capture effective (computed) z-index for timeline ordering

createTimelineElementFromManifestClip used `clip.zIndex ?? computed`, but the
runtime reports inline-only z-index (0 for CSS-rule authored z-index), and
`0 ?? x` keeps the 0 — so every CSS-styled clip collapsed to a z=0 tie. The
timeline then ordered rows by DOM position instead of true stacking, and the
first vertical drag renumbered the whole tie group, clobbering the author's
z-index. Prefer the effective computed read from the live element (the same
read the reorder commit uses); fall back to the runtime value only when the
element isn't live.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-08 23:46:25 -04:00
parent 5ce362299c
commit 03f8089e52
2 changed files with 39 additions and 1 deletions
@@ -102,6 +102,40 @@ describe("parseTimelineFromDOM — hfId from data-hf-id", () => {
expect(element.hidden).toBe(true); expect(element.hidden).toBe(true);
}); });
it("captures the effective z-index from the live element, not the runtime inline-only value", () => {
// The runtime reports inline-only z-index (0 for CSS-rule authored z-index),
// which must NOT override the live element's effective z-index — otherwise
// the timeline collapses every CSS-styled clip to a z=0 tie and mis-orders.
const doc = makeDoc(`
<div data-composition-id="root">
<div id="hero" class="clip" data-start="0" data-duration="5" style="z-index: 30"></div>
</div>
`);
const hostEl = doc.getElementById("hero");
const element = createTimelineElementFromManifestClip({
clip: {
id: "hero",
label: "Hero",
kind: "element",
tagName: "div",
start: 0,
duration: 5,
track: 0,
zIndex: 0,
compositionId: null,
parentCompositionId: null,
compositionSrc: null,
assetUrl: null,
},
fallbackIndex: 0,
doc,
hostEl,
});
expect(element.zIndex).toBe(30);
});
}); });
describe("createImplicitTimelineLayersFromDOM — hfId from data-hf-id", () => { describe("createImplicitTimelineLayersFromDOM — hfId from data-hf-id", () => {
@@ -155,7 +155,11 @@ export function createTimelineElementFromManifestClip(params: {
start: clip.start, start: clip.start,
duration: clip.duration, duration: clip.duration,
track: clip.track, track: clip.track,
zIndex: clip.zIndex ?? getTimelineElementZIndex(hostEl), // Prefer the effective (computed) z-index read from the live element — the
// same read the reorder commit uses — so CSS-rule z-index (not just inline)
// is captured. clip.zIndex from the runtime is inline-only (0 for CSS rules),
// so it can only serve as a fallback when the element isn't live.
zIndex: getTimelineElementZIndex(hostEl) ?? clip.zIndex ?? 0,
stackingContextId, stackingContextId,
parentCompositionId, parentCompositionId,
compositionAncestors, compositionAncestors,