mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 07:19:52 +00:00
* 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.
367 lines
11 KiB
TypeScript
367 lines
11 KiB
TypeScript
// fallow-ignore-file code-duplication
|
|
import { createContext, useCallback, useContext, useMemo, useRef, type ReactNode } from "react";
|
|
import type { useDomEditSession } from "../hooks/useDomEditSession";
|
|
|
|
type DomEditValue = ReturnType<typeof useDomEditSession>;
|
|
|
|
export interface DomEditActionsValue extends Pick<
|
|
DomEditValue,
|
|
| "handleTimelineElementSelect"
|
|
| "handlePreviewCanvasMouseDown"
|
|
| "handlePreviewCanvasPointerMove"
|
|
| "handlePreviewCanvasPointerLeave"
|
|
| "applyDomSelection"
|
|
| "clearDomSelection"
|
|
| "handleDomStyleCommit"
|
|
| "handleDomAttributeCommit"
|
|
| "handleDomAttributeLiveCommit"
|
|
| "handleDomHtmlAttributeCommit"
|
|
| "handleDomPathOffsetCommit"
|
|
| "handleDomGroupPathOffsetCommit"
|
|
| "handleDomZIndexReorderCommit"
|
|
| "handleDomBoxSizeCommit"
|
|
| "handleDomRotationCommit"
|
|
| "handleDomManualEditsReset"
|
|
| "handleDomTextCommit"
|
|
| "handleDomTextFieldStyleCommit"
|
|
| "handleDomAddTextField"
|
|
| "handleDomRemoveTextField"
|
|
| "handleAskAgent"
|
|
| "handleAgentModalSubmit"
|
|
| "handleBlockedDomMove"
|
|
| "handleDomManualDragStart"
|
|
| "handleDomEditElementDelete"
|
|
| "handleGroupSelection"
|
|
| "handleUngroupSelection"
|
|
| "setActiveGroupElement"
|
|
| "buildDomSelectionFromTarget"
|
|
| "buildDomSelectionForTimelineElement"
|
|
| "updateDomEditHoverSelection"
|
|
| "resolveImportedFontAsset"
|
|
| "setAgentModalOpen"
|
|
| "setAgentPromptSelectionContext"
|
|
| "setAgentModalAnchorPoint"
|
|
| "handleGsapUpdateProperty"
|
|
| "handleGsapUpdateMeta"
|
|
| "handleGsapDeleteAnimation"
|
|
| "handleGsapDeleteAllForElement"
|
|
| "handleGsapAddAnimation"
|
|
| "handleGsapAddProperty"
|
|
| "handleGsapRemoveProperty"
|
|
| "handleGsapUpdateFromProperty"
|
|
| "handleGsapAddFromProperty"
|
|
| "handleGsapRemoveFromProperty"
|
|
| "handleGsapAddKeyframe"
|
|
| "handleGsapAddKeyframeBatch"
|
|
| "handleGsapRemoveKeyframe"
|
|
| "handleGsapConvertToKeyframes"
|
|
| "handleGsapRemoveAllKeyframes"
|
|
| "handleResetSelectedElementKeyframes"
|
|
| "commitAnimatedProperty"
|
|
| "commitAnimatedProperties"
|
|
| "handleSetArcPath"
|
|
| "handleUpdateArcSegment"
|
|
| "handleUnroll"
|
|
| "invalidateGsapCache"
|
|
| "previewIframeRef"
|
|
| "commitMutation"
|
|
| "applyMarqueeSelection"
|
|
| "handleUpdateKeyframeEase"
|
|
| "handleSetAllKeyframeEases"
|
|
> {}
|
|
|
|
export interface DomEditSelectionValue extends Pick<
|
|
DomEditValue,
|
|
| "domEditSelection"
|
|
| "domEditGroupSelections"
|
|
| "domEditHoverSelection"
|
|
| "activeGroupElement"
|
|
| "domEditSelectionRef"
|
|
| "selectedGsapAnimations"
|
|
| "gsapMultipleTimelines"
|
|
| "gsapUnsupportedTimelinePattern"
|
|
| "agentModalOpen"
|
|
| "agentModalAnchorPoint"
|
|
| "copiedAgentPrompt"
|
|
| "agentPromptSelectionContext"
|
|
> {}
|
|
|
|
const DomEditActionsContext = createContext<DomEditActionsValue | null>(null);
|
|
const DomEditSelectionContext = createContext<DomEditSelectionValue | null>(null);
|
|
|
|
export function useDomEditActionsContext(): DomEditActionsValue {
|
|
const ctx = useContext(DomEditActionsContext);
|
|
if (!ctx) throw new Error("useDomEditActionsContext must be used within DomEditProvider");
|
|
return ctx;
|
|
}
|
|
|
|
export function useDomEditSelectionContext(): DomEditSelectionValue {
|
|
const ctx = useContext(DomEditSelectionContext);
|
|
if (!ctx) throw new Error("useDomEditSelectionContext must be used within DomEditProvider");
|
|
return ctx;
|
|
}
|
|
|
|
/** @deprecated Prefer useDomEditActionsContext or useDomEditSelectionContext. */
|
|
export function useDomEditContext(): DomEditValue {
|
|
return { ...useDomEditActionsContext(), ...useDomEditSelectionContext() };
|
|
}
|
|
|
|
export function DomEditProvider({
|
|
value: {
|
|
domEditSelection,
|
|
domEditGroupSelections,
|
|
domEditHoverSelection,
|
|
agentModalOpen,
|
|
agentModalAnchorPoint,
|
|
copiedAgentPrompt,
|
|
agentPromptSelectionContext,
|
|
domEditSelectionRef,
|
|
handleTimelineElementSelect,
|
|
handlePreviewCanvasMouseDown,
|
|
handlePreviewCanvasPointerMove,
|
|
handlePreviewCanvasPointerLeave,
|
|
applyDomSelection,
|
|
clearDomSelection,
|
|
handleDomStyleCommit,
|
|
handleDomAttributeCommit,
|
|
handleDomAttributeLiveCommit,
|
|
handleDomHtmlAttributeCommit,
|
|
handleDomPathOffsetCommit,
|
|
handleDomGroupPathOffsetCommit,
|
|
handleDomZIndexReorderCommit,
|
|
handleDomBoxSizeCommit,
|
|
handleDomRotationCommit,
|
|
handleDomManualEditsReset,
|
|
|
|
handleDomTextCommit,
|
|
handleDomTextFieldStyleCommit,
|
|
handleDomAddTextField,
|
|
handleDomRemoveTextField,
|
|
handleAskAgent,
|
|
handleAgentModalSubmit,
|
|
handleBlockedDomMove,
|
|
handleDomManualDragStart,
|
|
handleDomEditElementDelete,
|
|
handleGroupSelection,
|
|
handleUngroupSelection,
|
|
setActiveGroupElement,
|
|
activeGroupElement,
|
|
buildDomSelectionFromTarget,
|
|
buildDomSelectionForTimelineElement,
|
|
updateDomEditHoverSelection,
|
|
resolveImportedFontAsset,
|
|
setAgentModalOpen,
|
|
setAgentPromptSelectionContext,
|
|
setAgentModalAnchorPoint,
|
|
selectedGsapAnimations,
|
|
gsapMultipleTimelines,
|
|
gsapUnsupportedTimelinePattern,
|
|
handleGsapUpdateProperty,
|
|
handleGsapUpdateMeta,
|
|
handleGsapDeleteAnimation,
|
|
handleGsapDeleteAllForElement,
|
|
handleGsapAddAnimation,
|
|
handleGsapAddProperty,
|
|
handleGsapRemoveProperty,
|
|
handleGsapUpdateFromProperty,
|
|
handleGsapAddFromProperty,
|
|
handleGsapRemoveFromProperty,
|
|
handleGsapAddKeyframe,
|
|
handleGsapAddKeyframeBatch,
|
|
handleGsapRemoveKeyframe,
|
|
handleGsapConvertToKeyframes,
|
|
handleGsapRemoveAllKeyframes,
|
|
handleResetSelectedElementKeyframes,
|
|
commitAnimatedProperty,
|
|
commitAnimatedProperties,
|
|
handleSetArcPath,
|
|
handleUpdateArcSegment,
|
|
handleUnroll,
|
|
invalidateGsapCache,
|
|
previewIframeRef,
|
|
commitMutation,
|
|
applyMarqueeSelection,
|
|
handleUpdateKeyframeEase,
|
|
handleSetAllKeyframeEases,
|
|
},
|
|
children,
|
|
}: {
|
|
value: DomEditValue;
|
|
children: ReactNode;
|
|
}) {
|
|
const commitMutationRef = useRef(commitMutation);
|
|
commitMutationRef.current = commitMutation;
|
|
|
|
const stableCommitMutation = useCallback<DomEditActionsValue["commitMutation"]>(
|
|
(mutation, options) => commitMutationRef.current(mutation, options),
|
|
[],
|
|
);
|
|
|
|
const actions = useMemo<DomEditActionsValue>(
|
|
() => ({
|
|
handleTimelineElementSelect,
|
|
handlePreviewCanvasMouseDown,
|
|
handlePreviewCanvasPointerMove,
|
|
handlePreviewCanvasPointerLeave,
|
|
applyDomSelection,
|
|
clearDomSelection,
|
|
handleDomStyleCommit,
|
|
handleDomAttributeCommit,
|
|
handleDomAttributeLiveCommit,
|
|
handleDomHtmlAttributeCommit,
|
|
handleDomPathOffsetCommit,
|
|
handleDomGroupPathOffsetCommit,
|
|
handleDomZIndexReorderCommit,
|
|
handleDomBoxSizeCommit,
|
|
handleDomRotationCommit,
|
|
handleDomManualEditsReset,
|
|
handleDomTextCommit,
|
|
handleDomTextFieldStyleCommit,
|
|
handleDomAddTextField,
|
|
handleDomRemoveTextField,
|
|
handleAskAgent,
|
|
handleAgentModalSubmit,
|
|
handleBlockedDomMove,
|
|
handleDomManualDragStart,
|
|
handleDomEditElementDelete,
|
|
handleGroupSelection,
|
|
handleUngroupSelection,
|
|
setActiveGroupElement,
|
|
buildDomSelectionFromTarget,
|
|
buildDomSelectionForTimelineElement,
|
|
updateDomEditHoverSelection,
|
|
resolveImportedFontAsset,
|
|
setAgentModalOpen,
|
|
setAgentPromptSelectionContext,
|
|
setAgentModalAnchorPoint,
|
|
handleGsapUpdateProperty,
|
|
handleGsapUpdateMeta,
|
|
handleGsapDeleteAnimation,
|
|
handleGsapDeleteAllForElement,
|
|
handleGsapAddAnimation,
|
|
handleGsapAddProperty,
|
|
handleGsapRemoveProperty,
|
|
handleGsapUpdateFromProperty,
|
|
handleGsapAddFromProperty,
|
|
handleGsapRemoveFromProperty,
|
|
handleGsapAddKeyframe,
|
|
handleGsapAddKeyframeBatch,
|
|
handleGsapRemoveKeyframe,
|
|
handleGsapConvertToKeyframes,
|
|
handleGsapRemoveAllKeyframes,
|
|
handleResetSelectedElementKeyframes,
|
|
commitAnimatedProperty,
|
|
commitAnimatedProperties,
|
|
handleSetArcPath,
|
|
handleUpdateArcSegment,
|
|
handleUnroll,
|
|
invalidateGsapCache,
|
|
previewIframeRef,
|
|
commitMutation: stableCommitMutation,
|
|
applyMarqueeSelection,
|
|
handleUpdateKeyframeEase,
|
|
handleSetAllKeyframeEases,
|
|
}),
|
|
[
|
|
handleTimelineElementSelect,
|
|
handlePreviewCanvasMouseDown,
|
|
handlePreviewCanvasPointerMove,
|
|
handlePreviewCanvasPointerLeave,
|
|
applyDomSelection,
|
|
clearDomSelection,
|
|
handleDomStyleCommit,
|
|
handleDomAttributeCommit,
|
|
handleDomAttributeLiveCommit,
|
|
handleDomHtmlAttributeCommit,
|
|
handleDomPathOffsetCommit,
|
|
handleDomGroupPathOffsetCommit,
|
|
handleDomZIndexReorderCommit,
|
|
handleDomBoxSizeCommit,
|
|
handleDomRotationCommit,
|
|
handleDomManualEditsReset,
|
|
handleDomTextCommit,
|
|
handleDomTextFieldStyleCommit,
|
|
handleDomAddTextField,
|
|
handleDomRemoveTextField,
|
|
handleAskAgent,
|
|
handleAgentModalSubmit,
|
|
handleBlockedDomMove,
|
|
handleDomManualDragStart,
|
|
handleDomEditElementDelete,
|
|
handleGroupSelection,
|
|
handleUngroupSelection,
|
|
setActiveGroupElement,
|
|
buildDomSelectionFromTarget,
|
|
buildDomSelectionForTimelineElement,
|
|
updateDomEditHoverSelection,
|
|
resolveImportedFontAsset,
|
|
setAgentModalOpen,
|
|
setAgentPromptSelectionContext,
|
|
setAgentModalAnchorPoint,
|
|
handleGsapUpdateProperty,
|
|
handleGsapUpdateMeta,
|
|
handleGsapDeleteAnimation,
|
|
handleGsapDeleteAllForElement,
|
|
handleGsapAddAnimation,
|
|
handleGsapAddProperty,
|
|
handleGsapRemoveProperty,
|
|
handleGsapUpdateFromProperty,
|
|
handleGsapAddFromProperty,
|
|
handleGsapRemoveFromProperty,
|
|
handleGsapAddKeyframe,
|
|
handleGsapAddKeyframeBatch,
|
|
handleGsapRemoveKeyframe,
|
|
handleGsapConvertToKeyframes,
|
|
handleGsapRemoveAllKeyframes,
|
|
handleResetSelectedElementKeyframes,
|
|
commitAnimatedProperty,
|
|
commitAnimatedProperties,
|
|
handleSetArcPath,
|
|
handleUpdateArcSegment,
|
|
handleUnroll,
|
|
invalidateGsapCache,
|
|
previewIframeRef,
|
|
stableCommitMutation,
|
|
applyMarqueeSelection,
|
|
handleUpdateKeyframeEase,
|
|
handleSetAllKeyframeEases,
|
|
],
|
|
);
|
|
|
|
const selection = useMemo<DomEditSelectionValue>(
|
|
() => ({
|
|
domEditSelection,
|
|
domEditGroupSelections,
|
|
domEditHoverSelection,
|
|
activeGroupElement,
|
|
domEditSelectionRef,
|
|
selectedGsapAnimations,
|
|
gsapMultipleTimelines,
|
|
gsapUnsupportedTimelinePattern,
|
|
agentModalOpen,
|
|
agentModalAnchorPoint,
|
|
copiedAgentPrompt,
|
|
agentPromptSelectionContext,
|
|
}),
|
|
[
|
|
domEditSelection,
|
|
domEditGroupSelections,
|
|
domEditHoverSelection,
|
|
activeGroupElement,
|
|
domEditSelectionRef,
|
|
selectedGsapAnimations,
|
|
gsapMultipleTimelines,
|
|
gsapUnsupportedTimelinePattern,
|
|
agentModalOpen,
|
|
agentModalAnchorPoint,
|
|
copiedAgentPrompt,
|
|
agentPromptSelectionContext,
|
|
],
|
|
);
|
|
return (
|
|
<DomEditActionsContext value={actions}>
|
|
<DomEditSelectionContext value={selection}>{children}</DomEditSelectionContext>
|
|
</DomEditActionsContext>
|
|
);
|
|
}
|