@hyperframes/studio 0.6.58 → 0.6.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ import { useCallback, useMemo, useRef, useState, type DragEvent } from "react";
2
+ import {
3
+ STUDIO_INSPECTOR_PANELS_ENABLED,
4
+ STUDIO_MOTION_PANEL_ENABLED,
5
+ } from "../components/editor/manualEditingAvailability";
6
+ import { readStudioMotionFromElement } from "../components/editor/studioMotion";
7
+ import type { StudioContextValue } from "../contexts/StudioContext";
8
+ import type { DomEditSelection } from "../components/editor/domEditing";
9
+
10
+ interface StudioContextInput {
11
+ projectId: string;
12
+ activeCompPath: string | null;
13
+ setActiveCompPath: (path: string | null) => void;
14
+ showToast: (message: string, tone?: "error" | "info") => void;
15
+ previewIframeRef: React.MutableRefObject<HTMLIFrameElement | null>;
16
+ captionEditMode: boolean;
17
+ compositionLoading: boolean;
18
+ refreshKey: number;
19
+ setRefreshKey: React.Dispatch<React.SetStateAction<number>>;
20
+ currentTime: number;
21
+ timelineElements: StudioContextValue["timelineElements"];
22
+ isPlaying: boolean;
23
+ editHistory: { canUndo: boolean; canRedo: boolean; undoLabel: string; redoLabel: string };
24
+ handleUndo: StudioContextValue["handleUndo"];
25
+ handleRedo: StudioContextValue["handleRedo"];
26
+ renderQueue: {
27
+ jobs: unknown[];
28
+ isRendering: boolean;
29
+ deleteRender: (id: string) => void;
30
+ clearCompleted: () => void;
31
+ startRender: (options: unknown) => Promise<void>;
32
+ };
33
+ compositionDimensions: { width: number; height: number } | null;
34
+ waitForPendingDomEditSaves: () => Promise<void>;
35
+ handlePreviewIframeRef: (iframe: HTMLIFrameElement | null) => void;
36
+ refreshPreviewDocumentVersion: () => void;
37
+ timelineVisible: boolean;
38
+ toggleTimelineVisibility: () => void;
39
+ }
40
+
41
+ // fallow-ignore-next-line complexity
42
+ export function buildStudioContextValue(input: StudioContextInput): StudioContextValue {
43
+ return {
44
+ projectId: input.projectId,
45
+ activeCompPath: input.activeCompPath,
46
+ setActiveCompPath: input.setActiveCompPath,
47
+ showToast: input.showToast,
48
+ previewIframeRef: input.previewIframeRef,
49
+ captionEditMode: input.captionEditMode,
50
+ compositionLoading: input.compositionLoading,
51
+ refreshKey: input.refreshKey,
52
+ setRefreshKey: input.setRefreshKey,
53
+ currentTime: input.currentTime,
54
+ timelineElements: input.timelineElements,
55
+ isPlaying: input.isPlaying,
56
+ editHistory: input.editHistory,
57
+ handleUndo: input.handleUndo,
58
+ handleRedo: input.handleRedo,
59
+ renderQueue: input.renderQueue,
60
+ compositionDimensions: input.compositionDimensions,
61
+ waitForPendingDomEditSaves: input.waitForPendingDomEditSaves,
62
+ handlePreviewIframeRef: input.handlePreviewIframeRef,
63
+ refreshPreviewDocumentVersion: input.refreshPreviewDocumentVersion,
64
+ timelineVisible: input.timelineVisible,
65
+ toggleTimelineVisibility: input.toggleTimelineVisibility,
66
+ };
67
+ }
68
+
69
+ export interface InspectorState {
70
+ selectedStudioMotion: ReturnType<typeof readStudioMotionFromElement> | null;
71
+ layersPanelActive: boolean;
72
+ designPanelActive: boolean;
73
+ motionPanelActive: boolean;
74
+ inspectorPanelActive: boolean;
75
+ inspectorButtonActive: boolean;
76
+ shouldShowSelectedDomBounds: boolean;
77
+ }
78
+
79
+ export function useInspectorState(
80
+ rightPanelTab: string,
81
+ rightCollapsed: boolean,
82
+ isPlaying: boolean,
83
+ domEditSelection: DomEditSelection | null,
84
+ ): InspectorState {
85
+ // fallow-ignore-next-line complexity
86
+ return useMemo(() => {
87
+ const selectedStudioMotion =
88
+ STUDIO_INSPECTOR_PANELS_ENABLED && domEditSelection
89
+ ? readStudioMotionFromElement(domEditSelection.element)
90
+ : null;
91
+ const layersPanelActive = STUDIO_INSPECTOR_PANELS_ENABLED && rightPanelTab === "layers";
92
+ const designPanelActive = STUDIO_INSPECTOR_PANELS_ENABLED && rightPanelTab === "design";
93
+ const motionPanelActive =
94
+ STUDIO_INSPECTOR_PANELS_ENABLED && STUDIO_MOTION_PANEL_ENABLED && rightPanelTab === "motion";
95
+ const inspectorPanelActive = layersPanelActive || designPanelActive || motionPanelActive;
96
+ return {
97
+ selectedStudioMotion,
98
+ layersPanelActive,
99
+ designPanelActive,
100
+ motionPanelActive,
101
+ inspectorPanelActive,
102
+ inspectorButtonActive:
103
+ STUDIO_INSPECTOR_PANELS_ENABLED && !rightCollapsed && inspectorPanelActive,
104
+ shouldShowSelectedDomBounds: inspectorPanelActive && !rightCollapsed && !isPlaying,
105
+ };
106
+ }, [rightPanelTab, rightCollapsed, isPlaying, domEditSelection]);
107
+ }
108
+
109
+ // fallow-ignore-next-line complexity
110
+ export function useDragOverlay(onImportFiles: (files: FileList) => void) {
111
+ const [active, setActive] = useState(false);
112
+ const counterRef = useRef(0);
113
+ const onDragOver = useCallback((e: DragEvent) => {
114
+ if (!e.dataTransfer.types.includes("Files")) return;
115
+ e.preventDefault();
116
+ }, []);
117
+ const onDragEnter = useCallback((e: DragEvent) => {
118
+ if (!e.dataTransfer.types.includes("Files")) return;
119
+ e.preventDefault();
120
+ counterRef.current++;
121
+ setActive(true);
122
+ }, []);
123
+ const onDragLeave = useCallback(() => {
124
+ counterRef.current--;
125
+ if (counterRef.current === 0) setActive(false);
126
+ }, []);
127
+ const onDrop = useCallback(
128
+ (e: DragEvent) => {
129
+ counterRef.current = 0;
130
+ setActive(false);
131
+ if (e.defaultPrevented) return;
132
+ e.preventDefault();
133
+ if (e.dataTransfer.files.length) onImportFiles(e.dataTransfer.files);
134
+ },
135
+ [onImportFiles],
136
+ );
137
+ return { active, onDragOver, onDragEnter, onDragLeave, onDrop };
138
+ }
@@ -3,6 +3,7 @@ import { resolveVisualDomEditSelectionTarget } from "../components/editor/domEdi
3
3
  import {
4
4
  getDomLayerPatchTarget,
5
5
  isElementComputedVisible,
6
+ resolveAllVisualDomEditTargets,
6
7
  } from "../components/editor/domEditingElement";
7
8
  import { getEventTargetElement } from "./studioHelpers";
8
9
 
@@ -58,6 +59,7 @@ function removePointerEventsOverride(style: HTMLStyleElement | null): void {
58
59
  }
59
60
  }
60
61
 
62
+ // fallow-ignore-next-line complexity
61
63
  export function getPreviewTargetFromPointer(
62
64
  iframe: HTMLIFrameElement,
63
65
  clientX: number,
@@ -98,6 +100,42 @@ export function getPreviewTargetFromPointer(
98
100
  }
99
101
  }
100
102
 
103
+ /** Returns all independently-selectable elements at the pointer (topmost first). */
104
+ export function getAllPreviewTargetsFromPointer(
105
+ iframe: HTMLIFrameElement,
106
+ clientX: number,
107
+ clientY: number,
108
+ activeCompositionPath: string | null,
109
+ ): HTMLElement[] {
110
+ let doc: Document | null = null;
111
+ let win: Window | null = null;
112
+ try {
113
+ doc = iframe.contentDocument;
114
+ win = iframe.contentWindow;
115
+ } catch {
116
+ return [];
117
+ }
118
+ if (!doc || !win) return [];
119
+
120
+ const localPointer = resolvePreviewLocalPointer(iframe, doc, win, clientX, clientY);
121
+ if (!localPointer) return [];
122
+
123
+ const overrideStyle = forcePointerEventsAuto(doc);
124
+ try {
125
+ if (typeof doc.elementsFromPoint === "function") {
126
+ return resolveAllVisualDomEditTargets(doc.elementsFromPoint(localPointer.x, localPointer.y), {
127
+ activeCompositionPath,
128
+ });
129
+ }
130
+ const fallback = getEventTargetElement(doc.elementFromPoint(localPointer.x, localPointer.y));
131
+ if (!fallback || !getDomLayerPatchTarget(fallback, activeCompositionPath)) return [];
132
+ if (!isElementComputedVisible(fallback)) return [];
133
+ return [fallback];
134
+ } finally {
135
+ removePointerEventsOverride(overrideStyle);
136
+ }
137
+ }
138
+
101
139
  function objectLike(value: unknown): object | null {
102
140
  return value && (typeof value === "object" || typeof value === "function") ? value : null;
103
141
  }