mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
fix(studio): resolve lane-header removal against the clicked element
The lane-header keyframe toggle fires on whichever element owns the lane, which need not be the selected one. The remove path looked the animation up in the selected element's animations, so a non-selected element's flat tween missed and silently took the remove-one-keyframe branch, stranding the tween instead of deleting it.
This commit is contained in:
@@ -330,6 +330,41 @@ describe("useTimelineEditCallbacks — flat tween keyframe lanes", () => {
|
|||||||
view.unmount();
|
view.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The lane-header toggle fires on whichever element owns the lane, which need
|
||||||
|
// not be the selected one. Looking the flat tween up in the selected element's
|
||||||
|
// animations misses, and the miss silently takes the remove-one-keyframe
|
||||||
|
// branch, which strands the flat tween instead of deleting it.
|
||||||
|
it("removes a non-selected element's flat tween through that element's own animations", async () => {
|
||||||
|
const circle: TimelineElement = {
|
||||||
|
...element,
|
||||||
|
id: "circle",
|
||||||
|
key: "scenes/main.html#circle",
|
||||||
|
domId: "circle",
|
||||||
|
};
|
||||||
|
usePlayerStore.setState({
|
||||||
|
elements: [element, circle],
|
||||||
|
gsapAnimations: new Map([["scenes/main.html#circle", [otherFlatAnimation]]]),
|
||||||
|
});
|
||||||
|
const view = renderCallbacks();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await view.callbacks.onTogglePropertyGroupKeyframe?.(circle, {
|
||||||
|
animationId: otherFlatAnimation.id,
|
||||||
|
propertyGroup: "position",
|
||||||
|
tweenPercentage: 0,
|
||||||
|
properties: { x: 0 },
|
||||||
|
remove: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mocks.actions.handleGsapDeleteAnimation).toHaveBeenCalledWith(
|
||||||
|
otherFlatAnimation.id,
|
||||||
|
mocks.selection,
|
||||||
|
);
|
||||||
|
expect(mocks.actions.handleGsapRemoveKeyframe).not.toHaveBeenCalled();
|
||||||
|
view.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps authored interior deletion on the per-keyframe path", () => {
|
it("keeps authored interior deletion on the per-keyframe path", () => {
|
||||||
mocks.animations = [authoredInteriorAnimation()];
|
mocks.animations = [authoredInteriorAnimation()];
|
||||||
usePlayerStore.setState({ gsapAnimations: new Map([["box", mocks.animations]]) });
|
usePlayerStore.setState({ gsapAnimations: new Map([["box", mocks.animations]]) });
|
||||||
|
|||||||
@@ -324,10 +324,13 @@ export function useTimelineEditCallbacks({
|
|||||||
const selection = await buildDomSelectionForTimelineElement(element);
|
const selection = await buildDomSelectionForTimelineElement(element);
|
||||||
if (!selection) return;
|
if (!selection) return;
|
||||||
if (target.remove) {
|
if (target.remove) {
|
||||||
|
// The clicked element's animations, not the selected element's: this
|
||||||
|
// lookup decides delete-the-flat-tween vs remove-one-keyframe, and a
|
||||||
|
// miss silently takes the keyframe branch, stranding the flat tween.
|
||||||
removeKeyframeTarget(
|
removeKeyframeTarget(
|
||||||
target.animationId,
|
target.animationId,
|
||||||
target.tweenPercentage,
|
target.tweenPercentage,
|
||||||
selectedGsapAnimations,
|
resolveElementAnimations(element.key ?? element.id),
|
||||||
selection,
|
selection,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user