mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 15:20:13 +00:00
fix(studio): keep hidden state on expanded sub-composition rows
An expanded sub-composition child row is built from a manifest clip, which carries none of the host element's attributes, so data-hidden never reached it. The eye on that row therefore always reported the element visible: the first click hid it, and every click after wrote data-hidden again instead of removing it. The element could not be shown again, not even after a reload, because the attribute was already in the source. The flat store element for the same child is built with its host element, so the child row inherits hidden, timelineLocked and timelineRole from it.
This commit is contained in:
@@ -221,6 +221,35 @@ describe("buildExpandedElements", () => {
|
||||
expect(child.key).toBe(expectedStoreKey);
|
||||
});
|
||||
|
||||
// Regression: a child row is built from a manifest clip, which carries none of
|
||||
// the host element's attributes. Reading hidden off the manifest left every
|
||||
// expanded row reporting itself visible, so the eye wrote data-hidden a second
|
||||
// time instead of removing it and the element could never be shown again.
|
||||
it("inherits hidden and locked state from the flat store element", () => {
|
||||
const elements = [
|
||||
el({ id: "s1", domId: "s1", start: 0, duration: 14 }),
|
||||
el({
|
||||
id: "eyebrow",
|
||||
key: "index.html#eyebrow",
|
||||
domId: "eyebrow",
|
||||
start: 0,
|
||||
duration: 14,
|
||||
hidden: true,
|
||||
timelineLocked: true,
|
||||
}),
|
||||
];
|
||||
const manifest = [
|
||||
clip({ id: "s1", start: 0, duration: 14 }),
|
||||
clip({ id: "eyebrow", start: 0, duration: 14 }),
|
||||
];
|
||||
const parentMap = new Map([["eyebrow", "s1"]]);
|
||||
|
||||
const out = buildExpandedElements(elements, manifest, parentMap, "s1", "s1");
|
||||
const child = out.find((e) => e.domId === "eyebrow")!;
|
||||
expect(child.hidden).toBe(true);
|
||||
expect(child.timelineLocked).toBe(true);
|
||||
});
|
||||
|
||||
// Sub-comp internals (group + pills) have no data-start, so they're not in the
|
||||
// manifest. They arrive as DOM children and must still expand under their host.
|
||||
it("expands DOM-only sub-comp children (no manifest clip) under the host", () => {
|
||||
|
||||
@@ -134,6 +134,27 @@ interface DisplayBounds {
|
||||
track: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* State that lives on the live host element, not in the clip manifest:
|
||||
* `data-hidden`, `data-timeline-locked`, `data-timeline-role`. A child row is
|
||||
* built from a manifest clip with no hostEl to read, so
|
||||
* createTimelineElementFromManifestClip cannot see any of it. The flat store
|
||||
* element for the same child WAS built with one, so it is inherited from there.
|
||||
*
|
||||
* Without this the eye on an expanded child always reported the row visible, so
|
||||
* clicking it wrote data-hidden again instead of removing it, and a hidden child
|
||||
* could never be shown again (not even after a reload, since the attribute is in
|
||||
* the source).
|
||||
*/
|
||||
function hostElementState(flat: TimelineElement | undefined): Partial<TimelineElement> {
|
||||
if (!flat) return {};
|
||||
return {
|
||||
hidden: flat.hidden,
|
||||
timelineLocked: flat.timelineLocked,
|
||||
timelineRole: flat.timelineRole,
|
||||
};
|
||||
}
|
||||
|
||||
// `display` bounds come from the top-level scene clip (where the expanded row is
|
||||
// drawn). `editBasis` comes from the child's immediate sub-comp host: its absolute
|
||||
// start anchors local-time edits and its compositionSrc is the file edits write to.
|
||||
@@ -143,6 +164,7 @@ function buildChildElements(
|
||||
display: DisplayBounds,
|
||||
editBasis: { start: number; sourceFile: string | undefined },
|
||||
expandedHostKey: string,
|
||||
elements: readonly TimelineElement[],
|
||||
): TimelineElement[] {
|
||||
const result: TimelineElement[] = [];
|
||||
for (const child of siblings) {
|
||||
@@ -170,6 +192,7 @@ function buildChildElements(
|
||||
});
|
||||
result.push({
|
||||
...base,
|
||||
...hostElementState(elements.find((element) => element.key === key)),
|
||||
key,
|
||||
start: clamped.start,
|
||||
duration: clamped.duration,
|
||||
@@ -281,6 +304,7 @@ export function buildExpandedElements(
|
||||
},
|
||||
editBasis,
|
||||
parentKey,
|
||||
elements,
|
||||
);
|
||||
if (expanded.length === 0) return filterToTopLevel(elements, parentMap);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user