UNPKG

@remotion/studio

Version:

APIs for interacting with the Remotion Studio

473 lines (472 loc) 19.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TimelineDragHandler = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const player_1 = require("@remotion/player"); const react_1 = require("react"); const remotion_1 = require("remotion"); const get_left_of_timeline_slider_1 = require("../../helpers/get-left-of-timeline-slider"); const timeline_layout_1 = require("../../helpers/timeline-layout"); const in_out_1 = require("../../state/in-out"); const timeline_zoom_1 = require("../../state/timeline-zoom"); const z_index_1 = require("../../state/z-index"); const ContextMenu_1 = require("../ContextMenu"); const ForceSpecificCursor_1 = require("../ForceSpecificCursor"); const is_menu_item_1 = require("../Menu/is-menu-item"); const TimelineInOutToggle_1 = require("../TimelineInOutToggle"); const timeline_refs_1 = require("./timeline-refs"); const timeline_scroll_logic_1 = require("./timeline-scroll-logic"); const TimelineInOutPointer_1 = require("./TimelineInOutPointer"); const TimelineInOutPointerHandle_1 = require("./TimelineInOutPointerHandle"); const TimelineSlider_1 = require("./TimelineSlider"); const TimelineWidthProvider_1 = require("./TimelineWidthProvider"); const inner = { overflowY: 'auto', overflowX: 'hidden', }; const container = { userSelect: 'none', WebkitUserSelect: 'none', position: 'absolute', height: '100%', top: 0, }; const style = { width: '100%', height: '100%', userSelect: 'none', WebkitUserSelect: 'none', }; const getClientXWithScroll = (x) => { var _a; return x + ((_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollLeft); }; const TimelineDragHandler = () => { const video = remotion_1.Internals.useUnsafeVideoConfig(); const { zoom: zoomMap } = (0, react_1.useContext)(timeline_zoom_1.TimelineZoomCtx); const { canvasContent } = (0, react_1.useContext)(remotion_1.Internals.CompositionManager); const containerStyle = (0, react_1.useMemo)(() => { var _a; if (!canvasContent || canvasContent.type !== 'composition') { return {}; } const zoom = (_a = zoomMap[canvasContent.compositionId]) !== null && _a !== void 0 ? _a : timeline_zoom_1.TIMELINE_MIN_ZOOM; return { ...container, width: 100 * zoom + '%', }; }, [canvasContent, zoomMap]); if (!canvasContent || canvasContent.type !== 'composition') { return null; } return (jsx_runtime_1.jsx("div", { ref: timeline_refs_1.sliderAreaRef, style: containerStyle, children: video ? jsx_runtime_1.jsx(Inner, {}) : null })); }; exports.TimelineDragHandler = TimelineDragHandler; const Inner = () => { var _a; var _b, _c; const videoConfig = (0, remotion_1.useVideoConfig)(); const size = player_1.PlayerInternals.useElementSize(timeline_refs_1.scrollableRef, { triggerOnWindowResize: true, shouldApplyCssTransforms: true, }); const { isHighestContext } = (0, z_index_1.useZIndex)(); const setFrame = remotion_1.Internals.useTimelineSetFrame(); const [inOutDragging, setInOutDragging] = (0, react_1.useState)({ dragging: false, }); const timelineWidth = (0, react_1.useContext)(TimelineWidthProvider_1.TimelineWidthContext); const get = (0, react_1.useCallback)((frame) => { if (timelineWidth === null) { throw new Error('timeline width is not yet determined'); } return (0, get_left_of_timeline_slider_1.getXPositionOfItemInTimelineImperatively)(frame, videoConfig.durationInFrames, timelineWidth); }, [timelineWidth, videoConfig.durationInFrames]); const width = (_b = (_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.scrollWidth) !== null && _b !== void 0 ? _b : 0; const left = (_c = size === null || size === void 0 ? void 0 : size.left) !== null && _c !== void 0 ? _c : 0; const { inFrame, outFrame } = (0, in_out_1.useTimelineInOutFramePosition)(); const { setInAndOutFrames } = (0, in_out_1.useTimelineSetInOutFramePosition)(); const [dragging, setDragging] = (0, react_1.useState)({ dragging: false, }); const { playing, play, pause, seek } = player_1.PlayerInternals.usePlayer(); const scroller = (0, react_1.useRef)(null); const stopInterval = () => { if (scroller.current) { clearInterval(scroller.current); scroller.current = null; } }; const onPointerDown = (0, react_1.useCallback)((e) => { if (e.button !== 0) { return; } if (!isHighestContext) { return; } stopInterval(); if (!videoConfig) { return; } document.body.style.userSelect = 'none'; document.body.style.webkitUserSelect = 'none'; if (e.target === TimelineInOutPointerHandle_1.inPointerHandle.current) { if (inFrame === null) { throw new Error('expected outframe'); } const inMarker = get(inFrame); const outMarker = outFrame === null ? Infinity : get(outFrame - 1); (0, ForceSpecificCursor_1.forceSpecificCursor)('ew-resize'); setInOutDragging({ dragging: 'in', initialOffset: getClientXWithScroll(e.clientX), boundaries: [-Infinity, outMarker - inMarker], }); return; } if (e.target === TimelineInOutPointerHandle_1.outPointerHandle.current) { if (outFrame === null) { throw new Error('expected outframe'); } const outMarker = get(outFrame); const inMarker = inFrame === null ? -Infinity : get(inFrame + 1); (0, ForceSpecificCursor_1.forceSpecificCursor)('ew-resize'); setInOutDragging({ dragging: 'out', initialOffset: getClientXWithScroll(e.clientX), boundaries: [inMarker - outMarker, Infinity], }); return; } if (e.button !== 0) { return; } const frame = (0, timeline_scroll_logic_1.getFrameFromX)({ clientX: getClientXWithScroll(e.clientX) - left, durationInFrames: videoConfig.durationInFrames, width, extrapolate: 'clamp', }); seek(frame); setDragging({ dragging: true, wasPlaying: playing, }); pause(); }, [ isHighestContext, videoConfig, left, width, seek, playing, pause, inFrame, get, outFrame, ]); const onPointerMoveScrubbing = (0, react_1.useCallback)((e) => { var _a; if (!videoConfig) { return; } if (!dragging.dragging) { return; } const isRightOfArea = e.clientX >= ((_a = timeline_refs_1.scrollableRef.current) === null || _a === void 0 ? void 0 : _a.clientWidth) + left - timeline_layout_1.TIMELINE_PADDING; const isLeftOfArea = e.clientX <= left; const frame = (0, timeline_scroll_logic_1.getFrameFromX)({ clientX: getClientXWithScroll(e.clientX) - left, durationInFrames: videoConfig.durationInFrames, width, extrapolate: 'clamp', }); if (isLeftOfArea && (0, timeline_scroll_logic_1.canScrollTimelineIntoDirection)().canScrollLeft) { if (scroller.current) { return; } const scrollEvery = () => { var _a; if (!(0, timeline_scroll_logic_1.canScrollTimelineIntoDirection)().canScrollLeft) { stopInterval(); return; } const nextFrame = (0, timeline_scroll_logic_1.getFrameWhileScrollingLeft)({ durationInFrames: videoConfig.durationInFrames, width, }); const scrollPos = (0, timeline_scroll_logic_1.getScrollPositionForCursorOnLeftEdge)({ nextFrame, durationInFrames: videoConfig.durationInFrames, }); (_a = TimelineSlider_1.redrawTimelineSliderFast.current) === null || _a === void 0 ? void 0 : _a.draw(nextFrame); seek(nextFrame); (0, timeline_scroll_logic_1.scrollToTimelineXOffset)(scrollPos); }; scrollEvery(); scroller.current = setInterval(() => { scrollEvery(); }, 100); } else if (isRightOfArea && (0, timeline_scroll_logic_1.canScrollTimelineIntoDirection)().canScrollRight) { if (scroller.current) { return; } const scrollEvery = () => { var _a; if (!(0, timeline_scroll_logic_1.canScrollTimelineIntoDirection)().canScrollRight) { stopInterval(); return; } const nextFrame = (0, timeline_scroll_logic_1.getFrameWhileScrollingRight)({ durationInFrames: videoConfig.durationInFrames, width, }); const scrollPos = (0, timeline_scroll_logic_1.getScrollPositionForCursorOnRightEdge)({ nextFrame, durationInFrames: videoConfig.durationInFrames, }); (_a = TimelineSlider_1.redrawTimelineSliderFast.current) === null || _a === void 0 ? void 0 : _a.draw(nextFrame); seek(nextFrame); (0, timeline_scroll_logic_1.scrollToTimelineXOffset)(scrollPos); }; scrollEvery(); scroller.current = setInterval(() => { scrollEvery(); }, 100); } else { stopInterval(); seek(frame); } }, [videoConfig, dragging.dragging, left, width, seek]); const onPointerMoveInOut = (0, react_1.useCallback)((e) => { if (!videoConfig) { return; } if (!inOutDragging.dragging) { return; } const offset = Math.max(inOutDragging.boundaries[0], Math.min(inOutDragging.boundaries[1], getClientXWithScroll(e.clientX) - inOutDragging.initialOffset)); if (inOutDragging.dragging === 'in') { if (!TimelineInOutPointerHandle_1.inPointerHandle.current) { throw new Error('in pointer handle'); } if (!TimelineInOutPointer_1.inMarkerAreaRef.current) { throw new Error('expected inMarkerAreaRef'); } if (!inFrame) { throw new Error('expected inframes'); } TimelineInOutPointerHandle_1.inPointerHandle.current.style.transform = `translateX(${get(inFrame) + offset}px)`; TimelineInOutPointer_1.inMarkerAreaRef.current.style.width = String(get(inFrame) + offset) + 'px'; } if (inOutDragging.dragging === 'out') { if (!TimelineInOutPointerHandle_1.outPointerHandle.current) { throw new Error('in pointer handle'); } if (!TimelineInOutPointer_1.outMarkerAreaRef.current) { throw new Error('in outMarkerAreaRef'); } if (!outFrame) { throw new Error('expected outframes'); } TimelineInOutPointerHandle_1.outPointerHandle.current.style.transform = `translateX(${get(outFrame) + offset}px)`; TimelineInOutPointer_1.outMarkerAreaRef.current.style.left = String(get(outFrame) + offset) + 'px'; TimelineInOutPointer_1.outMarkerAreaRef.current.style.width = String(width - get(outFrame) - offset) + 'px'; } }, [get, inFrame, inOutDragging, outFrame, videoConfig, width]); const onPointerUpScrubbing = (0, react_1.useCallback)((e) => { stopInterval(); document.body.style.userSelect = ''; document.body.style.webkitUserSelect = ''; if (!videoConfig) { return; } if (!dragging.dragging) { return; } setDragging({ dragging: false, }); const frame = (0, timeline_scroll_logic_1.getFrameFromX)({ clientX: getClientXWithScroll(e.clientX) - left, durationInFrames: videoConfig.durationInFrames, width, extrapolate: 'clamp', }); setFrame((c) => { const newObj = { ...c, [videoConfig.id]: frame }; remotion_1.Internals.persistCurrentFrame(newObj); return newObj; }); if (dragging.wasPlaying) { play(); } }, [dragging, left, play, videoConfig, setFrame, width]); const onPointerUpInOut = (0, react_1.useCallback)((e) => { document.body.style.userSelect = ''; document.body.style.webkitUserSelect = ''; (0, ForceSpecificCursor_1.stopForcingSpecificCursor)(); if (!videoConfig) { return; } if (!inOutDragging.dragging) { return; } setInOutDragging({ dragging: false, }); const frame = (0, timeline_scroll_logic_1.getFrameFromX)({ clientX: getClientXWithScroll(e.clientX) - left, durationInFrames: videoConfig.durationInFrames, width, extrapolate: 'extend', }); if (inOutDragging.dragging === 'in') { if (frame < 1) { return setInAndOutFrames((prev) => { var _a; return ({ ...prev, [videoConfig.id]: { ...((_a = prev[videoConfig.id]) !== null && _a !== void 0 ? _a : TimelineInOutToggle_1.defaultInOutValue), inFrame: null, }, }); }); } const maxFrame = outFrame === null ? Infinity : outFrame - 1; setInAndOutFrames((prev) => { var _a; return ({ ...prev, [videoConfig.id]: { ...((_a = prev[videoConfig.id]) !== null && _a !== void 0 ? _a : TimelineInOutToggle_1.defaultInOutValue), inFrame: Math.min(maxFrame, frame), }, }); }); } else { if (frame > videoConfig.durationInFrames - 2) { return setInAndOutFrames((prev) => { var _a; return ({ ...prev, [videoConfig.id]: { ...((_a = prev[videoConfig.id]) !== null && _a !== void 0 ? _a : TimelineInOutToggle_1.defaultInOutValue), outFrame: null, }, }); }); } const minFrame = inFrame === null ? -Infinity : inFrame + 1; setInAndOutFrames((prev) => { var _a; return ({ ...prev, [videoConfig.id]: { ...((_a = prev[videoConfig.id]) !== null && _a !== void 0 ? _a : TimelineInOutToggle_1.defaultInOutValue), outFrame: Math.max(minFrame, frame), }, }); }); } }, [ inFrame, inOutDragging.dragging, left, outFrame, setInAndOutFrames, videoConfig, width, ]); (0, react_1.useEffect)(() => { if (!dragging.dragging) { return; } window.addEventListener('pointermove', onPointerMoveScrubbing); window.addEventListener('pointerup', onPointerUpScrubbing); return () => { window.removeEventListener('pointermove', onPointerMoveScrubbing); window.removeEventListener('pointerup', onPointerUpScrubbing); }; }, [dragging.dragging, onPointerMoveScrubbing, onPointerUpScrubbing]); (0, react_1.useEffect)(() => { if (inOutDragging.dragging === false) { return; } window.addEventListener('pointermove', onPointerMoveInOut); window.addEventListener('pointerup', onPointerUpInOut); return () => { window.removeEventListener('pointermove', onPointerMoveInOut); window.removeEventListener('pointerup', onPointerUpInOut); }; }, [inOutDragging.dragging, onPointerMoveInOut, onPointerUpInOut]); const inContextMenu = (0, react_1.useMemo)(() => { return [ { id: 'hide-in', keyHint: null, label: 'Clear In marker', leftItem: null, onClick: (_, e) => { e === null || e === void 0 ? void 0 : e.stopPropagation(); e === null || e === void 0 ? void 0 : e.preventDefault(); setInAndOutFrames((prev) => { var _a; return ({ ...prev, [videoConfig.id]: { ...((_a = prev[videoConfig.id]) !== null && _a !== void 0 ? _a : TimelineInOutToggle_1.defaultInOutValue), inFrame: null, }, }); }); }, quickSwitcherLabel: null, subMenu: null, type: 'item', value: 'hide-in', }, ]; }, [setInAndOutFrames, videoConfig.id]); const outContextMenu = (0, react_1.useMemo)(() => { return [ { id: 'hide-out', keyHint: null, label: 'Clear Out marker', leftItem: null, onClick: (_, e) => { e === null || e === void 0 ? void 0 : e.stopPropagation(); e === null || e === void 0 ? void 0 : e.preventDefault(); setInAndOutFrames((prev) => { var _a; return ({ ...prev, [videoConfig.id]: { ...((_a = prev[videoConfig.id]) !== null && _a !== void 0 ? _a : TimelineInOutToggle_1.defaultInOutValue), outFrame: null, }, }); }); }, quickSwitcherLabel: null, subMenu: null, type: 'item', value: 'hide-out', }, ]; }, [setInAndOutFrames, videoConfig.id]); return (jsx_runtime_1.jsxs("div", { style: style, onPointerDown: onPointerDown, children: [ jsx_runtime_1.jsx("div", { style: inner, className: is_menu_item_1.VERTICAL_SCROLLBAR_CLASSNAME }), inFrame !== null && (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: inContextMenu, children: jsx_runtime_1.jsx(TimelineInOutPointerHandle_1.TimelineInOutPointerHandle, { type: "in", atFrame: inFrame, dragging: inOutDragging.dragging === 'in' }) })), outFrame !== null && (jsx_runtime_1.jsx(ContextMenu_1.ContextMenu, { values: outContextMenu, children: jsx_runtime_1.jsx(TimelineInOutPointerHandle_1.TimelineInOutPointerHandle, { type: "out", dragging: inOutDragging.dragging === 'out', atFrame: outFrame }) }))] })); };