@remotion/studio 4.0.470 → 4.0.471
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.
- package/dist/components/ContextMenu.d.ts +7 -2
- package/dist/components/ContextMenu.js +50 -10
- package/dist/components/Preview.js +2 -1
- package/dist/components/SelectedOutlineOverlay.d.ts +31 -0
- package/dist/components/SelectedOutlineOverlay.js +100 -35
- package/dist/components/Timeline/Timeline.js +76 -1
- package/dist/components/Timeline/TimelineDeleteKeybindings.js +15 -0
- package/dist/components/Timeline/TimelineDragHandler.js +1 -0
- package/dist/components/Timeline/TimelineEffectItem.js +157 -4
- package/dist/components/Timeline/TimelineKeyframeControls.js +16 -11
- package/dist/components/Timeline/TimelineRowChrome.d.ts +3 -0
- package/dist/components/Timeline/TimelineRowChrome.js +3 -3
- package/dist/components/Timeline/TimelineSequenceItem.js +91 -1
- package/dist/components/Timeline/call-delete-keyframe.d.ts +16 -0
- package/dist/components/Timeline/call-delete-keyframe.js +86 -14
- package/dist/components/Timeline/delete-selected-keyframe.d.ts +10 -0
- package/dist/components/Timeline/delete-selected-keyframe.js +48 -7
- package/dist/components/Timeline/delete-selected-timeline-item.js +6 -11
- package/dist/components/Timeline/reset-selected-timeline-props.d.ts +38 -0
- package/dist/components/Timeline/reset-selected-timeline-props.js +143 -0
- package/dist/components/Timeline/sequence-props-subscription-store.d.ts +3 -2
- package/dist/components/Timeline/sequence-props-subscription-store.js +2 -1
- package/dist/components/Timeline/timeline-scroll-logic.js +3 -3
- package/dist/components/Timeline/use-sequence-props-subscription.js +2 -1
- package/dist/esm/{chunk-dny42qnq.js → chunk-z0z9d4r0.js} +1704 -962
- package/dist/esm/internals.mjs +1704 -962
- package/dist/esm/previewEntry.mjs +1711 -967
- package/dist/esm/renderEntry.mjs +1 -1
- package/dist/helpers/get-left-of-timeline-slider.js +1 -1
- package/dist/helpers/get-timeline-sequence-layout.js +10 -11
- package/dist/helpers/open-in-editor.d.ts +19 -1
- package/dist/helpers/open-in-editor.js +42 -4
- package/dist/helpers/use-menu-structure.js +0 -1
- package/dist/state/z-index.js +5 -2
- package/package.json +10 -10
|
@@ -14,11 +14,55 @@ const TimelineExpandArrowButton_1 = require("./TimelineExpandArrowButton");
|
|
|
14
14
|
const TimelineLayerEye_1 = require("./TimelineLayerEye");
|
|
15
15
|
const TimelineRowChrome_1 = require("./TimelineRowChrome");
|
|
16
16
|
const TimelineSelection_1 = require("./TimelineSelection");
|
|
17
|
+
const EFFECT_REORDER_MIME_TYPE = 'application/remotion-effect-reorder';
|
|
18
|
+
let currentEffectDrag = null;
|
|
17
19
|
const rowLabel = {
|
|
18
20
|
fontSize: 12,
|
|
19
21
|
color: 'rgba(255, 255, 255, 0.8)',
|
|
20
22
|
userSelect: 'none',
|
|
21
23
|
};
|
|
24
|
+
const rowStyle = {
|
|
25
|
+
height: timeline_layout_1.TREE_GROUP_ROW_HEIGHT,
|
|
26
|
+
cursor: 'default',
|
|
27
|
+
};
|
|
28
|
+
const reorderWrapper = {
|
|
29
|
+
position: 'relative',
|
|
30
|
+
};
|
|
31
|
+
const reorderLineBase = {
|
|
32
|
+
backgroundColor: '#0b84ff',
|
|
33
|
+
height: 2,
|
|
34
|
+
left: 0,
|
|
35
|
+
pointerEvents: 'none',
|
|
36
|
+
position: 'absolute',
|
|
37
|
+
right: 0,
|
|
38
|
+
zIndex: 1,
|
|
39
|
+
};
|
|
40
|
+
const hasEffectReorderDragType = (dataTransfer) => {
|
|
41
|
+
return Array.from(dataTransfer.types).includes(EFFECT_REORDER_MIME_TYPE);
|
|
42
|
+
};
|
|
43
|
+
const getEffectReorderDragData = (dataTransfer) => {
|
|
44
|
+
if (currentEffectDrag) {
|
|
45
|
+
return currentEffectDrag;
|
|
46
|
+
}
|
|
47
|
+
const value = dataTransfer.getData(EFFECT_REORDER_MIME_TYPE);
|
|
48
|
+
if (!value) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(value);
|
|
53
|
+
if (typeof parsed.nodePathKey === 'string' &&
|
|
54
|
+
typeof parsed.effectIndex === 'number') {
|
|
55
|
+
return parsed;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (_a) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
};
|
|
63
|
+
const getDestinationIndex = ({ fromIndex, insertionIndex, }) => {
|
|
64
|
+
return insertionIndex > fromIndex ? insertionIndex - 1 : insertionIndex;
|
|
65
|
+
};
|
|
22
66
|
const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, documentationLink, nodePath, validatedLocation, rowDepth, getIsExpanded, toggleTrack, }) => {
|
|
23
67
|
var _a;
|
|
24
68
|
var _b;
|
|
@@ -27,6 +71,7 @@ const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, do
|
|
|
27
71
|
const { codeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeCodeValuesContext);
|
|
28
72
|
const { setCodeValues } = (0, react_1.useContext)(remotion_1.Internals.VisualModeSettersContext);
|
|
29
73
|
const selection = (0, TimelineSelection_1.useTimelineRowSelection)(nodePathInfo);
|
|
74
|
+
const [dropIndicator, setDropIndicator] = (0, react_1.useState)(null);
|
|
30
75
|
const effectStatus = (0, react_1.useMemo)(() => remotion_1.Internals.getEffectCodeValuesCtx({
|
|
31
76
|
codeValues,
|
|
32
77
|
nodePath,
|
|
@@ -45,6 +90,11 @@ const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, do
|
|
|
45
90
|
const deleteDisabled = !previewConnected ||
|
|
46
91
|
effectStatus.type !== 'can-update-effect' ||
|
|
47
92
|
!validatedLocation.source;
|
|
93
|
+
const canReorder = TimelineSelection_1.SELECTION_ENABLED &&
|
|
94
|
+
previewConnected &&
|
|
95
|
+
effectStatus.type === 'can-update-effect' &&
|
|
96
|
+
Boolean(validatedLocation.source);
|
|
97
|
+
const nodePathKey = (0, react_1.useMemo)(() => remotion_1.Internals.makeSequencePropsSubscriptionKey(nodePath), [nodePath]);
|
|
48
98
|
const onDeleteEffectFromSource = (0, react_1.useCallback)(async () => {
|
|
49
99
|
if (deleteDisabled) {
|
|
50
100
|
return;
|
|
@@ -148,9 +198,6 @@ const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, do
|
|
|
148
198
|
validatedLocation.source,
|
|
149
199
|
]);
|
|
150
200
|
const isExpanded = getIsExpanded(nodePathInfo);
|
|
151
|
-
const rowStyle = (0, react_1.useMemo)(() => ({
|
|
152
|
-
height: timeline_layout_1.TREE_GROUP_ROW_HEIGHT,
|
|
153
|
-
}), []);
|
|
154
201
|
const labelStyle = (0, react_1.useMemo)(() => {
|
|
155
202
|
return {
|
|
156
203
|
...rowLabel,
|
|
@@ -164,7 +211,113 @@ const TimelineEffectItem = ({ label, nodePathInfo, effectIndex, effectSchema, do
|
|
|
164
211
|
paddingRight: timeline_layout_1.EXPANDED_SECTION_PADDING_RIGHT,
|
|
165
212
|
};
|
|
166
213
|
}, [selection.selected]);
|
|
214
|
+
const getDropTarget = (0, react_1.useCallback)((e) => {
|
|
215
|
+
const dragData = getEffectReorderDragData(e.dataTransfer);
|
|
216
|
+
if (!dragData || dragData.nodePathKey !== nodePathKey) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
220
|
+
const before = e.clientY < rect.top + rect.height / 2;
|
|
221
|
+
const insertionIndex = before ? effectIndex : effectIndex + 1;
|
|
222
|
+
const toIndex = getDestinationIndex({
|
|
223
|
+
fromIndex: dragData.effectIndex,
|
|
224
|
+
insertionIndex,
|
|
225
|
+
});
|
|
226
|
+
if (toIndex === dragData.effectIndex) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
dragData,
|
|
231
|
+
toIndex,
|
|
232
|
+
indicator: before ? 'before' : 'after',
|
|
233
|
+
};
|
|
234
|
+
}, [effectIndex, nodePathKey]);
|
|
235
|
+
const onDragStart = (0, react_1.useCallback)((e) => {
|
|
236
|
+
if (!canReorder) {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const dragData = { nodePathKey, effectIndex };
|
|
241
|
+
currentEffectDrag = dragData;
|
|
242
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
243
|
+
e.dataTransfer.setData(EFFECT_REORDER_MIME_TYPE, JSON.stringify(dragData));
|
|
244
|
+
e.stopPropagation();
|
|
245
|
+
}, [canReorder, effectIndex, nodePathKey]);
|
|
246
|
+
const onDragEnd = (0, react_1.useCallback)(() => {
|
|
247
|
+
currentEffectDrag = null;
|
|
248
|
+
setDropIndicator(null);
|
|
249
|
+
}, []);
|
|
250
|
+
const onDragOver = (0, react_1.useCallback)((e) => {
|
|
251
|
+
if (!canReorder || !hasEffectReorderDragType(e.dataTransfer)) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const dropTarget = getDropTarget(e);
|
|
255
|
+
if (!dropTarget) {
|
|
256
|
+
setDropIndicator(null);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
e.stopPropagation();
|
|
261
|
+
e.dataTransfer.dropEffect = 'move';
|
|
262
|
+
setDropIndicator(dropTarget.indicator);
|
|
263
|
+
}, [canReorder, getDropTarget]);
|
|
264
|
+
const onDragLeave = (0, react_1.useCallback)((e) => {
|
|
265
|
+
if (e.currentTarget.contains(e.relatedTarget)) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
setDropIndicator(null);
|
|
269
|
+
}, []);
|
|
270
|
+
const onDrop = (0, react_1.useCallback)(async (e) => {
|
|
271
|
+
if (!canReorder ||
|
|
272
|
+
previewServerState.type !== 'connected' ||
|
|
273
|
+
!validatedLocation.source) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const dropTarget = getDropTarget(e);
|
|
277
|
+
if (!dropTarget) {
|
|
278
|
+
setDropIndicator(null);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
e.preventDefault();
|
|
282
|
+
e.stopPropagation();
|
|
283
|
+
setDropIndicator(null);
|
|
284
|
+
currentEffectDrag = null;
|
|
285
|
+
try {
|
|
286
|
+
const result = await (0, call_api_1.callApi)('/api/reorder-effect', {
|
|
287
|
+
fileName: validatedLocation.source,
|
|
288
|
+
sequenceNodePath: nodePath,
|
|
289
|
+
fromIndex: dropTarget.dragData.effectIndex,
|
|
290
|
+
toIndex: dropTarget.toIndex,
|
|
291
|
+
clientId: previewServerState.clientId,
|
|
292
|
+
});
|
|
293
|
+
if (result.success) {
|
|
294
|
+
(0, NotificationCenter_1.showNotification)('Reordered effect', 2000);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
(0, NotificationCenter_1.showNotification)(result.reason, 4000);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
(0, NotificationCenter_1.showNotification)(err.message, 4000);
|
|
302
|
+
}
|
|
303
|
+
}, [
|
|
304
|
+
canReorder,
|
|
305
|
+
getDropTarget,
|
|
306
|
+
nodePath,
|
|
307
|
+
previewServerState,
|
|
308
|
+
validatedLocation.source,
|
|
309
|
+
]);
|
|
310
|
+
const reorderLineStyle = (0, react_1.useMemo)(() => {
|
|
311
|
+
if (!dropIndicator) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
...reorderLineBase,
|
|
316
|
+
...(dropIndicator === 'before' ? { top: -1 } : { bottom: -1 }),
|
|
317
|
+
};
|
|
318
|
+
}, [dropIndicator]);
|
|
167
319
|
const row = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: rowDepth, eye: canToggle ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: "effect", hidden: isDisabled, onInvoked: onToggle })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowButton, { isExpanded: isExpanded, onClick: () => toggleTrack(nodePathInfo), label: `${label} section`, disabled: false }), style: rowStyle, selected: selection.selected, selectable: selection.selectable, onSelect: selection.onSelect, showSelectedBackground: true, containsSelection: false, outerHeight: null, children: jsx_runtime_1.jsx("span", { title: label, style: labelStyle, children: label }) }));
|
|
168
|
-
|
|
320
|
+
const draggableRow = canReorder ? (jsx_runtime_1.jsxs("div", { draggable: true, onDragStart: onDragStart, onDragEnd: onDragEnd, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, style: reorderWrapper, children: [reorderLineStyle ? jsx_runtime_1.jsx("div", { style: reorderLineStyle }) : null, row] })) : (row);
|
|
321
|
+
return previewConnected ? (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: contextMenuValues, onOpen: selection.selectable ? selection.onSelect : null, children: draggableRow })) : (draggableRow);
|
|
169
322
|
};
|
|
170
323
|
exports.TimelineEffectItem = TimelineEffectItem;
|
|
@@ -15,7 +15,7 @@ const controlsContainerStyle = {
|
|
|
15
15
|
alignItems: 'center',
|
|
16
16
|
display: 'flex',
|
|
17
17
|
flexShrink: 0,
|
|
18
|
-
gap:
|
|
18
|
+
gap: 1,
|
|
19
19
|
marginRight: 4,
|
|
20
20
|
};
|
|
21
21
|
const navButtonStyle = {
|
|
@@ -26,19 +26,24 @@ const navButtonStyle = {
|
|
|
26
26
|
cursor: 'pointer',
|
|
27
27
|
display: 'flex',
|
|
28
28
|
flexShrink: 0,
|
|
29
|
-
fontSize: 8,
|
|
30
29
|
height: 12,
|
|
31
30
|
justifyContent: 'center',
|
|
32
31
|
lineHeight: 1,
|
|
33
32
|
outline: 'none',
|
|
34
33
|
padding: 0,
|
|
35
34
|
userSelect: 'none',
|
|
36
|
-
width:
|
|
35
|
+
width: 12,
|
|
37
36
|
};
|
|
38
37
|
const diamondButtonStyle = {
|
|
39
38
|
...navButtonStyle,
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
background: 'none',
|
|
40
|
+
};
|
|
41
|
+
const diamondIconStyle = {
|
|
42
|
+
borderRadius: 1,
|
|
43
|
+
boxShadow: '0 0 0 1px rgba(0, 0, 0, 0.4)',
|
|
44
|
+
height: 7,
|
|
45
|
+
transform: 'rotate(45deg)',
|
|
46
|
+
width: 7,
|
|
42
47
|
};
|
|
43
48
|
const svgStyle = { display: 'block' };
|
|
44
49
|
const getCurrentKeyframeValue = ({ propStatus, jsxFrame, defaultValue, dragOverrideValue, }) => {
|
|
@@ -203,15 +208,15 @@ const TimelineKeyframeControls = ({ fieldKey, propStatus, nodePath, fileName, ke
|
|
|
203
208
|
}), [nextDisabled]);
|
|
204
209
|
const diamondStyle = (0, react_1.useMemo)(() => ({
|
|
205
210
|
...diamondButtonStyle,
|
|
206
|
-
backgroundColor: hasKeyframeAtCurrentFrame ? colors_1.BLUE : colors_1.LIGHT_TEXT,
|
|
207
|
-
borderRadius: 1,
|
|
208
|
-
boxShadow: '0 0 0 1px rgba(0, 0, 0, 0.4)',
|
|
209
211
|
cursor: canToggleKeyframe && clientId ? 'pointer' : 'default',
|
|
210
212
|
opacity: canToggleKeyframe && clientId ? 1 : 0.35,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
}), [canToggleKeyframe, clientId]);
|
|
214
|
+
const diamondIcon = (0, react_1.useMemo)(() => ({
|
|
215
|
+
...diamondIconStyle,
|
|
216
|
+
backgroundColor: hasKeyframeAtCurrentFrame ? colors_1.BLUE : colors_1.LIGHT_TEXT,
|
|
217
|
+
}), [hasKeyframeAtCurrentFrame]);
|
|
213
218
|
return (jsx_runtime_1.jsxs("div", { style: controlsContainerStyle, children: [
|
|
214
|
-
jsx_runtime_1.jsx("button", { type: "button", style: previousStyle, disabled: previousDisabled, onPointerDown: previousDisabled ? undefined : onPrevious, "aria-label": "Go to previous keyframe", title: "Previous keyframe", children: jsx_runtime_1.jsx("svg", { width: "
|
|
219
|
+
jsx_runtime_1.jsx("button", { type: "button", style: previousStyle, disabled: previousDisabled, onPointerDown: previousDisabled ? undefined : onPrevious, "aria-label": "Go to previous keyframe", title: "Previous keyframe", children: jsx_runtime_1.jsx("svg", { width: "14", height: "14", viewBox: "0 0 10 10", style: svgStyle, children: jsx_runtime_1.jsx("path", { d: "M7 1.5L3 5L7 8.5Z", fill: "#ccc" }) }) }), jsx_runtime_1.jsx("button", { type: "button", style: diamondStyle, disabled: !canToggleKeyframe || !clientId, onPointerDown: canToggleKeyframe && clientId ? onToggleKeyframe : undefined, "aria-label": hasKeyframeAtCurrentFrame ? 'Remove keyframe' : 'Add keyframe', title: hasKeyframeAtCurrentFrame ? 'Remove keyframe' : 'Add keyframe', children: jsx_runtime_1.jsx("span", { style: diamondIcon }) }), jsx_runtime_1.jsx("button", { type: "button", style: nextStyle, disabled: nextDisabled, onPointerDown: nextDisabled ? undefined : onNext, "aria-label": "Go to next keyframe", title: "Next keyframe", children: jsx_runtime_1.jsx("svg", { width: "14", height: "14", viewBox: "0 0 10 10", style: svgStyle, children: jsx_runtime_1.jsx("path", { d: "M3 1.5L7 5L3 8.5Z", fill: "#ccc" }) }) })
|
|
215
220
|
] }));
|
|
216
221
|
};
|
|
217
222
|
exports.TimelineKeyframeControls = TimelineKeyframeControls;
|
|
@@ -13,5 +13,8 @@ export declare const TimelineRowChrome: React.FC<{
|
|
|
13
13
|
readonly showSelectedBackground: boolean;
|
|
14
14
|
readonly containsSelection: boolean;
|
|
15
15
|
readonly outerHeight: number | null;
|
|
16
|
+
readonly onDragLeave?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
17
|
+
readonly onDragOver?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
18
|
+
readonly onDrop?: (e: React.DragEvent<HTMLDivElement>) => void;
|
|
16
19
|
readonly onDoubleClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
|
17
20
|
}>;
|
|
@@ -25,7 +25,7 @@ const keyframeControlsColumnStyle = {
|
|
|
25
25
|
marginRight: -(timeline_row_layout_1.TIMELINE_KEYFRAME_CONTROLS_WIDTH - timeline_row_layout_1.TIMELINE_ROW_BASE_PADDING),
|
|
26
26
|
width: timeline_row_layout_1.TIMELINE_KEYFRAME_CONTROLS_WIDTH,
|
|
27
27
|
};
|
|
28
|
-
const TimelineRowChrome = ({ depth, eye, keyframeControls, arrow, children, style, selected, selectable, onSelect, showSelectedBackground, containsSelection, outerHeight, onDoubleClick, }) => {
|
|
28
|
+
const TimelineRowChrome = ({ depth, eye, keyframeControls, arrow, children, style, selected, selectable, onSelect, showSelectedBackground, containsSelection, outerHeight, onDragLeave, onDragOver, onDrop, onDoubleClick, }) => {
|
|
29
29
|
const indentWidth = (0, timeline_row_layout_1.getTimelineRowIndentWidth)(depth);
|
|
30
30
|
const chromeColumnStyle = (0, react_1.useMemo)(() => ({
|
|
31
31
|
alignItems: 'center',
|
|
@@ -72,8 +72,8 @@ const TimelineRowChrome = ({ depth, eye, keyframeControls, arrow, children, styl
|
|
|
72
72
|
jsx_runtime_1.jsxs("div", { style: leftChromeStyle, children: [keyframeControls ? (jsx_runtime_1.jsx("div", { style: keyframeControlsColumnStyle, children: keyframeControls })) : null, jsx_runtime_1.jsxs("div", { style: chromeColumnStyle, children: [eye, indentWidth > 0 ? jsx_runtime_1.jsx(Padder_1.Padder, { depth: depth }) : null, arrow] })
|
|
73
73
|
] }), children] }));
|
|
74
74
|
if (outerStyle) {
|
|
75
|
-
return (jsx_runtime_1.jsx("div", { style: outerStyle, onPointerDown: selectable ? onPointerDown : undefined, onContextMenu: selectable ? onContextMenu : undefined, onDoubleClick: onDoubleClick, children: jsx_runtime_1.jsx("div", { style: innerRowStyle, children: chrome }) }));
|
|
75
|
+
return (jsx_runtime_1.jsx("div", { style: outerStyle, onDragLeave: onDragLeave, onDragOver: onDragOver, onDrop: onDrop, onPointerDown: selectable ? onPointerDown : undefined, onContextMenu: selectable ? onContextMenu : undefined, onDoubleClick: onDoubleClick, children: jsx_runtime_1.jsx("div", { style: innerRowStyle, children: chrome }) }));
|
|
76
76
|
}
|
|
77
|
-
return (jsx_runtime_1.jsx("div", { onPointerDown: selectable ? onPointerDown : undefined, onContextMenu: selectable ? onContextMenu : undefined, onDoubleClick: onDoubleClick, style: innerRowStyle, children: chrome }));
|
|
77
|
+
return (jsx_runtime_1.jsx("div", { onDragLeave: onDragLeave, onDragOver: onDragOver, onDrop: onDrop, onPointerDown: selectable ? onPointerDown : undefined, onContextMenu: selectable ? onContextMenu : undefined, onDoubleClick: onDoubleClick, style: innerRowStyle, children: chrome }));
|
|
78
78
|
};
|
|
79
79
|
exports.TimelineRowChrome = TimelineRowChrome;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TimelineSequenceItem = void 0;
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const studio_shared_1 = require("@remotion/studio-shared");
|
|
5
6
|
const react_1 = require("react");
|
|
6
7
|
const remotion_1 = require("remotion");
|
|
7
8
|
const no_react_1 = require("remotion/no-react");
|
|
@@ -31,6 +32,33 @@ const labelContainerStyle = {
|
|
|
31
32
|
minWidth: 0,
|
|
32
33
|
gap: 4,
|
|
33
34
|
};
|
|
35
|
+
const effectDropHighlight = {
|
|
36
|
+
backgroundColor: 'rgba(0, 155, 255, 0.16)',
|
|
37
|
+
outline: '1px solid rgba(0, 155, 255, 0.75)',
|
|
38
|
+
outlineOffset: -1,
|
|
39
|
+
};
|
|
40
|
+
const hasEffectDragType = (dataTransfer) => {
|
|
41
|
+
return Array.from(dataTransfer.types).some((type) => type === studio_shared_1.EFFECT_DRAG_MIME_TYPE ||
|
|
42
|
+
type === 'application/json' ||
|
|
43
|
+
type === 'text/plain');
|
|
44
|
+
};
|
|
45
|
+
const getEffectDragData = (dataTransfer) => {
|
|
46
|
+
for (const type of [
|
|
47
|
+
studio_shared_1.EFFECT_DRAG_MIME_TYPE,
|
|
48
|
+
'application/json',
|
|
49
|
+
'text/plain',
|
|
50
|
+
]) {
|
|
51
|
+
const value = dataTransfer.getData(type);
|
|
52
|
+
if (!value) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const parsed = (0, studio_shared_1.parseEffectDragData)(value);
|
|
56
|
+
if (parsed) {
|
|
57
|
+
return parsed;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
34
62
|
const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDisplayOffset }) => {
|
|
35
63
|
var _a;
|
|
36
64
|
var _b;
|
|
@@ -44,6 +72,7 @@ const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDis
|
|
|
44
72
|
const { setCanvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionSetters);
|
|
45
73
|
const { onSelect, selectable, selected } = (0, TimelineSelection_1.useTimelineRowSelection)(nodePathInfo);
|
|
46
74
|
const containsSelection = (0, TimelineSelection_1.useTimelineRowContainsSelection)(nodePathInfo);
|
|
75
|
+
const [effectDropHovered, setEffectDropHovered] = (0, react_1.useState)(false);
|
|
47
76
|
const { canOpenInEditor, openInEditor, originalLocation } = (0, use_open_sequence_in_editor_1.useOpenSequenceInEditor)(sequence);
|
|
48
77
|
const fileLocation = (0, react_1.useMemo)(() => (0, format_file_location_1.formatFileLocation)({
|
|
49
78
|
location: originalLocation,
|
|
@@ -322,6 +351,14 @@ const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDis
|
|
|
322
351
|
flexShrink: 0,
|
|
323
352
|
};
|
|
324
353
|
}, []);
|
|
354
|
+
const rowStyle = (0, react_1.useMemo)(() => {
|
|
355
|
+
return effectDropHovered
|
|
356
|
+
? {
|
|
357
|
+
...inner,
|
|
358
|
+
...effectDropHighlight,
|
|
359
|
+
}
|
|
360
|
+
: inner;
|
|
361
|
+
}, [effectDropHovered, inner]);
|
|
325
362
|
const hasExpandableContent = Boolean(sequence.controls) || sequence.effects.length > 0;
|
|
326
363
|
const canToggleVisibility = previewConnected &&
|
|
327
364
|
Boolean(sequence.controls) &&
|
|
@@ -330,7 +367,60 @@ const TimelineSequenceItem = ({ nestedDepth, sequence, nodePathInfo, keyframeDis
|
|
|
330
367
|
codeHiddenStatus !== undefined &&
|
|
331
368
|
codeHiddenStatus !== null &&
|
|
332
369
|
codeHiddenStatus.canUpdate;
|
|
333
|
-
const
|
|
370
|
+
const canDropEffect = previewServerState.type === 'connected' &&
|
|
371
|
+
nodePath !== null &&
|
|
372
|
+
validatedLocation !== null &&
|
|
373
|
+
sequence.type !== 'audio';
|
|
374
|
+
const onEffectDragOver = (0, react_1.useCallback)((e) => {
|
|
375
|
+
if (!canDropEffect || !hasEffectDragType(e.dataTransfer)) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
e.preventDefault();
|
|
379
|
+
e.dataTransfer.dropEffect = 'copy';
|
|
380
|
+
setEffectDropHovered(true);
|
|
381
|
+
}, [canDropEffect]);
|
|
382
|
+
const onEffectDragLeave = (0, react_1.useCallback)((e) => {
|
|
383
|
+
if (e.currentTarget.contains(e.relatedTarget)) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
setEffectDropHovered(false);
|
|
387
|
+
}, []);
|
|
388
|
+
const onEffectDrop = (0, react_1.useCallback)(async (e) => {
|
|
389
|
+
if (!canDropEffect ||
|
|
390
|
+
previewServerState.type !== 'connected' ||
|
|
391
|
+
nodePath === null ||
|
|
392
|
+
validatedLocation === null) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
e.preventDefault();
|
|
396
|
+
e.stopPropagation();
|
|
397
|
+
setEffectDropHovered(false);
|
|
398
|
+
const dragData = getEffectDragData(e.dataTransfer);
|
|
399
|
+
if (!dragData) {
|
|
400
|
+
(0, NotificationCenter_1.showNotification)('Could not read effect drag data', 3000);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
const result = await (0, call_api_1.callApi)('/api/add-effect', {
|
|
405
|
+
fileName: validatedLocation.source,
|
|
406
|
+
sequenceNodePath: nodePath,
|
|
407
|
+
effectName: dragData.effect.name,
|
|
408
|
+
effectImportPath: dragData.effect.importPath,
|
|
409
|
+
effectConfig: dragData.effect.config,
|
|
410
|
+
clientId: previewServerState.clientId,
|
|
411
|
+
});
|
|
412
|
+
if (result.success) {
|
|
413
|
+
(0, NotificationCenter_1.showNotification)(`Added ${dragData.effect.name}()`, 2000);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
(0, NotificationCenter_1.showNotification)(result.reason, 4000);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
(0, NotificationCenter_1.showNotification)(err.message, 4000);
|
|
421
|
+
}
|
|
422
|
+
}, [canDropEffect, nodePath, previewServerState, validatedLocation]);
|
|
423
|
+
const trackRow = (jsx_runtime_1.jsx(TimelineRowChrome_1.TimelineRowChrome, { depth: nestedDepth, eye: canToggleVisibility ? (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEye, { type: sequence.type === 'audio' ? 'speaker' : 'eye', hidden: isItemHidden, onInvoked: onToggleVisibility })) : (jsx_runtime_1.jsx(TimelineLayerEye_1.TimelineLayerEyeSpacer, {})), arrow: hasExpandableContent ? (jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowButton, { isExpanded: isExpanded, onClick: onToggleExpand, label: "track properties", disabled: !previewConnected || nodePathInfo === null })) : (jsx_runtime_1.jsx(TimelineExpandArrowButton_1.TimelineExpandArrowSpacer, {})), style: rowStyle, selected: selected, selectable: selectable, onSelect: onSelect, showSelectedBackground: true, containsSelection: containsSelection, outerHeight: outerHeight, onDragLeave: canDropEffect ? onEffectDragLeave : undefined, onDragOver: canDropEffect ? onEffectDragOver : undefined, onDrop: canDropEffect ? onEffectDrop : undefined, onDoubleClick: TimelineSelection_1.SELECTION_ENABLED && canOpenInEditor
|
|
334
424
|
? onShowInEditorDoubleClick
|
|
335
425
|
: undefined, children: jsx_runtime_1.jsxs("div", { style: labelContainerStyle, children: [
|
|
336
426
|
jsx_runtime_1.jsx(TimelineSequenceName_1.TimelineSequenceName, { sequence: sequence, selected: selected, containsSelection: containsSelection }), mediaSrc ? jsx_runtime_1.jsx(TimelineMediaInfo_1.TimelineMediaInfo, { src: mediaSrc }) : null, jsx_runtime_1.jsx(TimelineItemStack_1.TimelineItemStack, { originalLocation: originalLocation })
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import type { SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
|
|
2
2
|
import type { SetCodeValues } from './save-sequence-prop';
|
|
3
|
+
export type DeleteSequenceKeyframeChange = {
|
|
4
|
+
fileName: string;
|
|
5
|
+
nodePath: SequencePropsSubscriptionKey;
|
|
6
|
+
fieldKey: string;
|
|
7
|
+
sourceFrame: number;
|
|
8
|
+
schema: SequenceSchema;
|
|
9
|
+
};
|
|
10
|
+
export type DeleteEffectKeyframeChange = DeleteSequenceKeyframeChange & {
|
|
11
|
+
effectIndex: number;
|
|
12
|
+
};
|
|
3
13
|
export declare const callDeleteSequenceKeyframe: ({ fileName, nodePath, fieldKey, sourceFrame, schema, setCodeValues, clientId, }: {
|
|
4
14
|
fileName: string;
|
|
5
15
|
nodePath: SequencePropsSubscriptionKey;
|
|
@@ -19,3 +29,9 @@ export declare const callDeleteEffectKeyframe: ({ fileName, nodePath, effectInde
|
|
|
19
29
|
setCodeValues: SetCodeValues;
|
|
20
30
|
clientId: string;
|
|
21
31
|
}) => Promise<void>;
|
|
32
|
+
export declare const callDeleteKeyframes: ({ sequenceKeyframes, effectKeyframes, setCodeValues, clientId, }: {
|
|
33
|
+
sequenceKeyframes: DeleteSequenceKeyframeChange[];
|
|
34
|
+
effectKeyframes: DeleteEffectKeyframeChange[];
|
|
35
|
+
setCodeValues: SetCodeValues;
|
|
36
|
+
clientId: string;
|
|
37
|
+
}) => Promise<void>;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.callDeleteEffectKeyframe = exports.callDeleteSequenceKeyframe = void 0;
|
|
3
|
+
exports.callDeleteKeyframes = exports.callDeleteEffectKeyframe = exports.callDeleteSequenceKeyframe = void 0;
|
|
4
4
|
const studio_shared_1 = require("@remotion/studio-shared");
|
|
5
5
|
const call_api_1 = require("../call-api");
|
|
6
6
|
const save_prop_queue_1 = require("./save-prop-queue");
|
|
7
|
+
const groupByNodePath = (keyframes) => {
|
|
8
|
+
var _a;
|
|
9
|
+
const groups = new Map();
|
|
10
|
+
for (const keyframe of keyframes) {
|
|
11
|
+
const key = JSON.stringify(keyframe.nodePath);
|
|
12
|
+
const group = (_a = groups.get(key)) !== null && _a !== void 0 ? _a : [];
|
|
13
|
+
group.push(keyframe);
|
|
14
|
+
groups.set(key, group);
|
|
15
|
+
}
|
|
16
|
+
return [...groups.values()];
|
|
17
|
+
};
|
|
7
18
|
const callDeleteSequenceKeyframe = ({ fileName, nodePath, fieldKey, sourceFrame, schema, setCodeValues, clientId, }) => {
|
|
8
19
|
return (0, save_prop_queue_1.enqueueSavePropChange)({
|
|
9
20
|
nodePath,
|
|
@@ -13,12 +24,17 @@ const callDeleteSequenceKeyframe = ({ fileName, nodePath, fieldKey, sourceFrame,
|
|
|
13
24
|
fieldKey,
|
|
14
25
|
frame: sourceFrame,
|
|
15
26
|
}),
|
|
16
|
-
apiCall: () => (0, call_api_1.callApi)('/api/delete-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
apiCall: () => (0, call_api_1.callApi)('/api/delete-keyframes', {
|
|
28
|
+
sequenceKeyframes: [
|
|
29
|
+
{
|
|
30
|
+
fileName,
|
|
31
|
+
nodePath,
|
|
32
|
+
key: fieldKey,
|
|
33
|
+
frame: sourceFrame,
|
|
34
|
+
schema,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
effectKeyframes: [],
|
|
22
38
|
clientId,
|
|
23
39
|
}),
|
|
24
40
|
errorLabel: 'Could not delete keyframe',
|
|
@@ -35,16 +51,72 @@ const callDeleteEffectKeyframe = ({ fileName, nodePath, effectIndex, fieldKey, s
|
|
|
35
51
|
fieldKey,
|
|
36
52
|
frame: sourceFrame,
|
|
37
53
|
}),
|
|
38
|
-
apiCall: () => (0, call_api_1.callApi)('/api/delete-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
apiCall: () => (0, call_api_1.callApi)('/api/delete-keyframes', {
|
|
55
|
+
sequenceKeyframes: [],
|
|
56
|
+
effectKeyframes: [
|
|
57
|
+
{
|
|
58
|
+
fileName,
|
|
59
|
+
sequenceNodePath: nodePath,
|
|
60
|
+
effectIndex,
|
|
61
|
+
key: fieldKey,
|
|
62
|
+
frame: sourceFrame,
|
|
63
|
+
schema,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
45
66
|
clientId,
|
|
46
67
|
}),
|
|
47
68
|
errorLabel: 'Could not delete keyframe',
|
|
48
69
|
});
|
|
49
70
|
};
|
|
50
71
|
exports.callDeleteEffectKeyframe = callDeleteEffectKeyframe;
|
|
72
|
+
const callDeleteKeyframes = ({ sequenceKeyframes, effectKeyframes, setCodeValues, clientId, }) => {
|
|
73
|
+
if (sequenceKeyframes.length === 0 && effectKeyframes.length === 0) {
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
}
|
|
76
|
+
for (const keyframes of groupByNodePath(sequenceKeyframes)) {
|
|
77
|
+
const [firstKeyframe] = keyframes;
|
|
78
|
+
if (!firstKeyframe) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
setCodeValues(firstKeyframe.nodePath, (prev) => (0, studio_shared_1.optimisticDeleteSequenceKeyframes)({
|
|
82
|
+
previous: prev,
|
|
83
|
+
keyframes: keyframes.map((keyframe) => ({
|
|
84
|
+
fieldKey: keyframe.fieldKey,
|
|
85
|
+
frame: keyframe.sourceFrame,
|
|
86
|
+
})),
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
for (const keyframes of groupByNodePath(effectKeyframes)) {
|
|
90
|
+
const [firstKeyframe] = keyframes;
|
|
91
|
+
if (!firstKeyframe) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
setCodeValues(firstKeyframe.nodePath, (prev) => (0, studio_shared_1.optimisticDeleteEffectKeyframes)({
|
|
95
|
+
previous: prev,
|
|
96
|
+
keyframes: keyframes.map((keyframe) => ({
|
|
97
|
+
effectIndex: keyframe.effectIndex,
|
|
98
|
+
fieldKey: keyframe.fieldKey,
|
|
99
|
+
frame: keyframe.sourceFrame,
|
|
100
|
+
})),
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
return (0, call_api_1.callApi)('/api/delete-keyframes', {
|
|
104
|
+
sequenceKeyframes: sequenceKeyframes.map((keyframe) => ({
|
|
105
|
+
fileName: keyframe.fileName,
|
|
106
|
+
nodePath: keyframe.nodePath,
|
|
107
|
+
key: keyframe.fieldKey,
|
|
108
|
+
frame: keyframe.sourceFrame,
|
|
109
|
+
schema: keyframe.schema,
|
|
110
|
+
})),
|
|
111
|
+
effectKeyframes: effectKeyframes.map((keyframe) => ({
|
|
112
|
+
fileName: keyframe.fileName,
|
|
113
|
+
sequenceNodePath: keyframe.nodePath,
|
|
114
|
+
effectIndex: keyframe.effectIndex,
|
|
115
|
+
key: keyframe.fieldKey,
|
|
116
|
+
frame: keyframe.sourceFrame,
|
|
117
|
+
schema: keyframe.schema,
|
|
118
|
+
})),
|
|
119
|
+
clientId,
|
|
120
|
+
}).then(() => undefined);
|
|
121
|
+
};
|
|
122
|
+
exports.callDeleteKeyframes = callDeleteKeyframes;
|
|
@@ -9,3 +9,13 @@ export declare const deleteSelectedKeyframe: ({ nodePathInfo, frame, sequences,
|
|
|
9
9
|
setCodeValues: SetCodeValues;
|
|
10
10
|
clientId: string;
|
|
11
11
|
}) => Promise<void> | null;
|
|
12
|
+
export declare const deleteSelectedKeyframes: ({ keyframes, sequences, overrideIdsToNodePaths, setCodeValues, clientId, }: {
|
|
13
|
+
keyframes: {
|
|
14
|
+
nodePathInfo: SequenceNodePathInfo;
|
|
15
|
+
frame: number;
|
|
16
|
+
}[];
|
|
17
|
+
sequences: TSequence[];
|
|
18
|
+
overrideIdsToNodePaths: OverrideIdToNodePaths;
|
|
19
|
+
setCodeValues: SetCodeValues;
|
|
20
|
+
clientId: string;
|
|
21
|
+
}) => Promise<void> | null;
|