mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 10:14:30 +00:00
The diamond context menu still declared `onChangeEase` and `onCopyProperties` props, and `TimelineOverlays` still threaded `onChangeKeyframeEase` plus a `keyframeCache` it never read. Nothing on any timeline branch calls them, so they are removed along with the `onChangeKeyframeEase` callback implementation. Also from review: - `deleteSelectedKeyframes` only falls back to the sole keyframed animation when there is exactly one. A collapsed selection key carries no animation id, so taking the first of several deleted an arbitrary tween's keyframe. - The duration-less retime test asserts the real 87.601% instead of `expect.any(Number)`, so a wrong timing basis fails it. - `Timeline` wires the keyframe handlers' `onSelectSegment` through to the diamonds; it was built and then dropped, so segment ease selection never fired. - The flat text section arms auto-focus in state rather than reading and clearing a ref during render, which Strict Mode's double render swallowed.
52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
import { usePlayerStore } from "../player/store/playerStore";
|
|
import { timelineKeyframeTargetFromSelectionKey } from "../player/components/timelineKeyframeIdentity";
|
|
import type { CommitMutationOptions } from "./gsapScriptCommitTypes";
|
|
|
|
let deleteKeyframesCommitCounter = 0;
|
|
|
|
/**
|
|
* Remove the keyframes currently selected in the player store from the active
|
|
* element's GSAP animation. Reads selection lazily so it stays correct when
|
|
* invoked from a ref callback.
|
|
*/
|
|
export function deleteSelectedKeyframes(session: {
|
|
selectedGsapAnimations: readonly { id: string; keyframes?: unknown }[];
|
|
handleGsapRemoveKeyframe: (
|
|
animId: string,
|
|
pct: number,
|
|
options?: Partial<CommitMutationOptions>,
|
|
) => void;
|
|
}): void {
|
|
const { selectedKeyframes, selectedElementId } = usePlayerStore.getState();
|
|
if (!selectedElementId) return;
|
|
const keyframedAnimations = session.selectedGsapAnimations.filter((anim) => anim.keyframes);
|
|
// A collapsed selection key (an ungrouped animation) carries no animation id,
|
|
// so it only resolves when there is exactly one keyframed animation it could
|
|
// mean. Taking the first of several deletes an arbitrary tween's keyframe.
|
|
const fallbackAnimation = keyframedAnimations.length === 1 ? keyframedAnimations[0] : undefined;
|
|
const animationsById = new Map(keyframedAnimations.map((animation) => [animation.id, animation]));
|
|
const removals = new Map<string, { animationId: string; percentage: number }>();
|
|
for (const key of selectedKeyframes) {
|
|
const target = timelineKeyframeTargetFromSelectionKey(selectedElementId, key);
|
|
if (!target) continue;
|
|
const animation = target.animationId
|
|
? animationsById.get(target.animationId)
|
|
: fallbackAnimation;
|
|
if (!animation) continue;
|
|
const percentage = target.tweenPercentage ?? target.percentage;
|
|
removals.set(`${animation.id}\0${percentage}`, { animationId: animation.id, percentage });
|
|
}
|
|
const targets = [...removals.values()];
|
|
if (targets.length === 0) return;
|
|
const coalesceOptions = {
|
|
coalesceKey: `delete-keyframes:${++deleteKeyframesCommitCounter}`,
|
|
coalesceMs: Number.POSITIVE_INFINITY,
|
|
};
|
|
for (const [index, target] of targets.entries()) {
|
|
session.handleGsapRemoveKeyframe(target.animationId, target.percentage, {
|
|
...coalesceOptions,
|
|
...(index === targets.length - 1 ? { softReload: true } : { skipReload: true }),
|
|
});
|
|
}
|
|
}
|