mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
fix(studio): cover GSAP editor target-resolution limitations (#1116)
Follow-up to #1115. Makes the Design-panel editor recognise every target shape real compositions use. The panel stays behind STUDIO_GSAP_PANEL_ENABLED (default off) — no flag change here. - Array targets: tl.to([a, b], {...}) resolves to a CSS group selector (".a, .b"). The source array is never rewritten — the joined string is for display/matching only; edits still touch just the vars object. - Chained calls: tl.to(a, ...).to(b, ...) — the matcher now walks the member chain to its timeline root, so every link is captured (previously only the first). Deletion is chain-aware: it splices out the single targeted link and re-points the chain instead of dropping the whole statement. - gsap.utils.toArray("sel") resolves like querySelectorAll, inline or via a variable binding. - Lexical scoping: element-variable resolution is now per-scope (walks the enclosing function/program chain) instead of a flat map. Fixes silent wrong-resolution when two IIFEs reuse a variable name, and unlocks multi-scene files. (Addresses review: flat-binding-scope.) - forEach/map callback params (items.forEach(el => tl.to(el, …))) and items[i] indexing resolve to the collection's selector, so loop-generated tweens are editable. - Panel matching: an element matches a tween when its id/selector is any member of a comma-group target, so either element of an array/toArray tween surfaces the shared animation. - Review items: mutation parse failures now console.warn instead of swallowing silently; buildTweenStatementCode no longer emits duration on `set`; the id-only serialize-side filter is renamed getAnimationsForElementId to disambiguate from the panel's id-or-selector matcher; added fromTo round-trip and variable-target overlap-lint tests. Genuinely runtime-only targets (template-literal selectors, unbounded loops) still skip gracefully — they can't be resolved or matched statically.
This commit is contained in:
@@ -37,4 +37,13 @@ describe("getAnimationsForElement", () => {
|
||||
expect(getAnimationsForElement(animations, {})).toEqual([]);
|
||||
expect(getAnimationsForElement(animations, { id: null, selector: null })).toEqual([]);
|
||||
});
|
||||
|
||||
it("matches an element that is one member of a group-selector tween", () => {
|
||||
// Array/toArray targets serialize as a CSS group selector; selecting either
|
||||
// member element should surface the shared tween.
|
||||
const grouped = [anim(".clock-face, .clock-hand")];
|
||||
expect(getAnimationsForElement(grouped, { selector: ".clock-face" })).toHaveLength(1);
|
||||
expect(getAnimationsForElement(grouped, { selector: ".clock-hand" })).toHaveLength(1);
|
||||
expect(getAnimationsForElement(grouped, { selector: ".unrelated" })).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,9 +9,11 @@ export interface GsapElementTarget {
|
||||
|
||||
/**
|
||||
* A tween belongs to the selected element when its target selector addresses
|
||||
* that element — either by id (`#id`) or by the exact CSS selector the element
|
||||
* was selected through (`.kicker`). Real compositions target tweens by class
|
||||
* via `querySelector`, so id-only matching misses them.
|
||||
* that element — by id (`#id`), by the exact CSS selector the element was
|
||||
* selected through (`.kicker`), or as one member of a group selector
|
||||
* (`.clock-face, .clock-hand`, emitted for array/`toArray` targets). Real
|
||||
* compositions target tweens by class via `querySelector`, so id-only matching
|
||||
* misses them.
|
||||
*/
|
||||
export function getAnimationsForElement(
|
||||
animations: GsapAnimation[],
|
||||
@@ -21,7 +23,9 @@ export function getAnimationsForElement(
|
||||
if (target.id) matchers.add(`#${target.id}`);
|
||||
if (target.selector) matchers.add(target.selector);
|
||||
if (matchers.size === 0) return [];
|
||||
return animations.filter((a) => matchers.has(a.targetSelector));
|
||||
return animations.filter((a) =>
|
||||
a.targetSelector.split(",").some((part) => matchers.has(part.trim())),
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchParsedAnimations(
|
||||
|
||||
Reference in New Issue
Block a user