mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat(studio): element groups — studio UI (#1759)
* feat(studio): element groups — source mutations Wrap/unwrap source mutations (group geometry, the wrap-elements / unwrap-elements routes) that the studio group feature is built on. Studio UI lands in the next PR. * fix(studio): hoverable group interior + non-sticky drill-in Two group selection bugs with animated members: 1) Empty space inside a group's overlay didn't hover/select the group. Members animated outside the wrapper's static box (110px box vs 340px member union), so elementsFromPoint hit only the full-bleed background there. Add a member-union hit-test fallback: a point inside a group's live member bounds resolves to that group (innermost wins). 2) After drilling into a group and selecting a child, nothing else was selectable — out-of-scope resolved to null. Make drill-in non-sticky: interacting outside the drilled group re-resolves normally and exits the drill-in, so a later click on the group selects it as a unit again.
This commit is contained in:
@@ -186,6 +186,34 @@ export function resolveDomEditGroupOverlayRect(rects: OverlayRect[]): OverlayRec
|
||||
};
|
||||
}
|
||||
|
||||
// A group's overlay box encompasses its members' actual rendered bounds, not just
|
||||
// the wrapper's own box — so members moved or transformed out of the wrapper still
|
||||
// sit inside the box. Used by the selection, hover, and off-canvas overlays so they
|
||||
// all agree on where a group is.
|
||||
export function groupAwareOverlayRect(
|
||||
overlayEl: HTMLDivElement,
|
||||
iframe: HTMLIFrameElement,
|
||||
el: HTMLElement,
|
||||
): OverlayRect | null {
|
||||
const rect = toOverlayRect(overlayEl, iframe, el);
|
||||
if (!rect || !el.hasAttribute("data-hf-group")) return rect;
|
||||
// Union the MEMBERS' rendered rects — where the content actually is — not the
|
||||
// wrapper's own box. The wrapper is invisible and its box can sit apart from the
|
||||
// members once they've been moved/transformed, which would otherwise drag the
|
||||
// group's bounds (and its off-canvas marker) off to a stale position.
|
||||
const rects: OverlayRect[] = [];
|
||||
for (const child of Array.from(el.children)) {
|
||||
const childRect = toOverlayRect(overlayEl, iframe, child as HTMLElement);
|
||||
if (childRect) rects.push(childRect);
|
||||
}
|
||||
const union = rects.length > 0 ? resolveDomEditGroupOverlayRect(rects) : null;
|
||||
if (!union) return rect; // empty group → fall back to the wrapper box
|
||||
// resolveDomEditGroupOverlayRect hardcodes editScaleX/Y to 1; keep the wrapper's
|
||||
// real edit (display) scale, which the drag uses to convert pointer→offset — a
|
||||
// reset-to-1 makes the group move at ~display-scale speed and lag the cursor.
|
||||
return { ...union, editScaleX: rect.editScaleX, editScaleY: rect.editScaleY };
|
||||
}
|
||||
|
||||
export function filterNestedDomEditGroupItems<T extends { element: HTMLElement }>(items: T[]): T[] {
|
||||
return items.filter(
|
||||
(item) => !items.some((other) => other !== item && other.element.contains(item.element)),
|
||||
|
||||
Reference in New Issue
Block a user