@remotion/player 4.0.0-oops.3 → 4.0.0-prefetch.11

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.
Files changed (41) hide show
  1. package/dist/MediaVolumeSlider.js +1 -1
  2. package/dist/Player.d.ts +16 -6
  3. package/dist/Player.js +23 -7
  4. package/dist/PlayerControls.d.ts +17 -2
  5. package/dist/PlayerControls.js +13 -5
  6. package/dist/PlayerSeekBar.d.ts +4 -0
  7. package/dist/PlayerSeekBar.js +35 -11
  8. package/dist/PlayerUI.d.ts +11 -2
  9. package/dist/PlayerUI.js +93 -12
  10. package/dist/calculate-scale.d.ts +10 -4
  11. package/dist/calculate-scale.js +13 -4
  12. package/dist/emitter-context.d.ts +1 -1
  13. package/dist/error-boundary.js +1 -1
  14. package/dist/event-emitter.d.ts +5 -0
  15. package/dist/event-emitter.js +4 -0
  16. package/dist/icons.js +8 -8
  17. package/dist/index.d.ts +20 -7
  18. package/dist/index.js +2 -1
  19. package/dist/player-methods.d.ts +2 -2
  20. package/dist/test/test-utils.d.ts +3 -3
  21. package/dist/test/test-utils.js +7 -3
  22. package/dist/test/validate-in-out-frames.test.d.ts +1 -0
  23. package/dist/test/validate-in-out-frames.test.js +55 -0
  24. package/dist/test/validate-prop.test.js +14 -14
  25. package/dist/use-playback.d.ts +4 -1
  26. package/dist/use-playback.js +4 -3
  27. package/dist/use-player.d.ts +9 -4
  28. package/dist/use-player.js +23 -4
  29. package/dist/utils/calculate-player-size.d.ts +2 -2
  30. package/dist/utils/preview-size.d.ts +8 -1
  31. package/dist/utils/use-cancellable-promises.d.ts +1 -1
  32. package/dist/utils/use-click-prevention-on-double-click.d.ts +1 -1
  33. package/dist/utils/use-element-size.d.ts +4 -0
  34. package/dist/utils/use-element-size.js +8 -0
  35. package/dist/utils/validate-in-out-frame.d.ts +6 -0
  36. package/dist/utils/validate-in-out-frame.js +54 -0
  37. package/dist/utils/validate-initial-frame.d.ts +4 -0
  38. package/dist/utils/validate-initial-frame.js +27 -0
  39. package/package.json +5 -5
  40. package/dist/utils/browser-supports-fullscreen.d.ts +0 -11
  41. package/dist/utils/browser-supports-fullscreen.js +0 -5
@@ -88,6 +88,6 @@ const MediaVolumeSlider = () => {
88
88
  }
89
89
  setMediaMuted((mute) => !mute);
90
90
  }, [mediaVolume, setMediaMuted, setMediaVolume]);
91
- return ((0, jsx_runtime_1.jsxs)("div", { ref: parentDivRef, style: parentDivStyle, children: [(0, jsx_runtime_1.jsx)("button", { "aria-label": isMutedOrZero ? 'Unmute sound' : 'Mute sound', title: isMutedOrZero ? 'Unmute sound' : 'Mute sound', onClick: onClick, onBlur: onBlur, onFocus: () => setFocused(true), style: volumeContainer, type: "button", children: isMutedOrZero ? (0, jsx_runtime_1.jsx)(icons_1.VolumeOffIcon, {}, void 0) : (0, jsx_runtime_1.jsx)(icons_1.VolumeOnIcon, {}, void 0) }, void 0), (focused || hover) && !mediaMuted ? ((0, jsx_runtime_1.jsx)("input", { ref: inputRef, "aria-label": "Change volume", className: player_css_classname_1.VOLUME_SLIDER_INPUT_CSS_CLASSNAME, max: 1, min: 0, onBlur: () => setFocused(false), onChange: onVolumeChange, step: 0.01, type: "range", value: mediaVolume }, void 0)) : null] }, void 0));
91
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: parentDivRef, style: parentDivStyle, children: [(0, jsx_runtime_1.jsx)("button", { "aria-label": isMutedOrZero ? 'Unmute sound' : 'Mute sound', title: isMutedOrZero ? 'Unmute sound' : 'Mute sound', onClick: onClick, onBlur: onBlur, onFocus: () => setFocused(true), style: volumeContainer, type: "button", children: isMutedOrZero ? (0, jsx_runtime_1.jsx)(icons_1.VolumeOffIcon, {}) : (0, jsx_runtime_1.jsx)(icons_1.VolumeOnIcon, {}) }), (focused || hover) && !mediaMuted ? ((0, jsx_runtime_1.jsx)("input", { ref: inputRef, "aria-label": "Change volume", className: player_css_classname_1.VOLUME_SLIDER_INPUT_CSS_CLASSNAME, max: 1, min: 0, onBlur: () => setFocused(false), onChange: onVolumeChange, step: 0.01, type: "range", value: mediaVolume })) : null] }));
92
92
  };
93
93
  exports.MediaVolumeSlider = MediaVolumeSlider;
package/dist/Player.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import React, { ComponentType, MutableRefObject } from 'react';
2
- import { CompProps } from 'remotion';
3
- import { PlayerRef } from './player-methods';
4
- import { RenderLoading } from './PlayerUI';
1
+ import type { ComponentType, MutableRefObject } from 'react';
2
+ import React from 'react';
3
+ import type { CompProps } from 'remotion';
4
+ import type { PlayerRef } from './player-methods';
5
+ import type { RenderLoading, RenderPoster } from './PlayerUI';
5
6
  declare type PropsIfHasProps<Props> = {} extends Props ? {
6
7
  inputProps?: Props;
7
8
  } : {
@@ -28,9 +29,18 @@ export declare type PlayerProps<T> = {
28
29
  numberOfSharedAudioTags?: number;
29
30
  playbackRate?: number;
30
31
  renderLoading?: RenderLoading;
32
+ moveToBeginningWhenEnded?: boolean;
33
+ className?: string;
34
+ initialFrame?: number;
35
+ renderPoster?: RenderPoster;
36
+ showPosterWhenPaused?: boolean;
37
+ showPosterWhenEnded?: boolean;
38
+ showPosterWhenUnplayed?: boolean;
39
+ inFrame?: number | null;
40
+ outFrame?: number | null;
31
41
  } & PropsIfHasProps<T> & CompProps<T>;
32
- export declare const componentOrNullIfLazy: <T>(props: CompProps<T>) => React.ComponentType<T> | null;
33
- export declare const PlayerFn: <T>({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls, loop, autoPlay, showVolumeControls, allowFullscreen, clickToPlay, doubleClickToFullscreen, spaceKeyToPlayOrPause, numberOfSharedAudioTags, errorFallback, playbackRate, renderLoading, ...componentProps }: PlayerProps<T>, ref: MutableRefObject<PlayerRef>) => JSX.Element;
42
+ export declare const componentOrNullIfLazy: <T>(props: CompProps<T>) => ComponentType<T> | null;
43
+ export declare const PlayerFn: <T>({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls, loop, autoPlay, showVolumeControls, allowFullscreen, clickToPlay, doubleClickToFullscreen, spaceKeyToPlayOrPause, moveToBeginningWhenEnded, numberOfSharedAudioTags, errorFallback, playbackRate, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, initialFrame, renderPoster, inFrame, outFrame, ...componentProps }: PlayerProps<T>, ref: MutableRefObject<PlayerRef>) => JSX.Element;
34
44
  declare module 'react' {
35
45
  function forwardRef<T, P = {}>(render: (props: P, ref: React.MutableRefObject<T>) => React.ReactElement | null): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
36
46
  }
package/dist/Player.js CHANGED
@@ -11,9 +11,10 @@ const emitter_context_1 = require("./emitter-context");
11
11
  const event_emitter_1 = require("./event-emitter");
12
12
  const player_css_classname_1 = require("./player-css-classname");
13
13
  const PlayerUI_1 = __importDefault(require("./PlayerUI"));
14
+ const validate_in_out_frame_1 = require("./utils/validate-in-out-frame");
15
+ const validate_initial_frame_1 = require("./utils/validate-initial-frame");
14
16
  const validate_playbackrate_1 = require("./utils/validate-playbackrate");
15
17
  const volume_persistance_1 = require("./volume-persistance");
16
- remotion_1.Internals.CSSUtils.injectCSS(remotion_1.Internals.CSSUtils.makeDefaultCSS(`.${player_css_classname_1.PLAYER_CSS_CLASSNAME}`, '#fff'));
17
18
  const componentOrNullIfLazy = (props) => {
18
19
  if ('component' in props) {
19
20
  return props.component;
@@ -21,7 +22,7 @@ const componentOrNullIfLazy = (props) => {
21
22
  return null;
22
23
  };
23
24
  exports.componentOrNullIfLazy = componentOrNullIfLazy;
24
- const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls = false, loop = false, autoPlay = false, showVolumeControls = true, allowFullscreen = true, clickToPlay, doubleClickToFullscreen = false, spaceKeyToPlayOrPause = true, numberOfSharedAudioTags = 5, errorFallback = () => '⚠️', playbackRate = 1, renderLoading, ...componentProps }, ref) => {
25
+ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps, inputProps, style, controls = false, loop = false, autoPlay = false, showVolumeControls = true, allowFullscreen = true, clickToPlay, doubleClickToFullscreen = false, spaceKeyToPlayOrPause = true, moveToBeginningWhenEnded = true, numberOfSharedAudioTags = 5, errorFallback = () => '⚠️', playbackRate = 1, renderLoading, className, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, initialFrame, renderPoster, inFrame, outFrame, ...componentProps }, ref) => {
25
26
  if (typeof window !== 'undefined') {
26
27
  // eslint-disable-next-line react-hooks/rules-of-hooks
27
28
  (0, react_1.useLayoutEffect)(() => {
@@ -41,7 +42,8 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
41
42
  throw new TypeError(`'component' must not be the 'Composition' component. Pass your own React component directly, and set the duration, fps and dimensions as separate props. See https://www.remotion.dev/docs/player/examples for an example.`);
42
43
  }
43
44
  const component = remotion_1.Internals.useLazyComponent(componentProps);
44
- const [frame, setFrame] = (0, react_1.useState)(0);
45
+ (0, validate_initial_frame_1.validateInitialFrame)({ initialFrame, durationInFrames });
46
+ const [frame, setFrame] = (0, react_1.useState)(() => initialFrame !== null && initialFrame !== void 0 ? initialFrame : 0);
45
47
  const [playing, setPlaying] = (0, react_1.useState)(false);
46
48
  const [rootId] = (0, react_1.useState)('player-comp');
47
49
  const [emitter] = (0, react_1.useState)(() => new event_emitter_1.PlayerEmitter());
@@ -59,7 +61,12 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
59
61
  remotion_1.Internals.validateDimension(compositionHeight, 'compositionHeight', 'of the <Player /> component');
60
62
  remotion_1.Internals.validateDimension(compositionWidth, 'compositionWidth', 'of the <Player /> component');
61
63
  remotion_1.Internals.validateDurationInFrames(durationInFrames, 'of the <Player/> component');
62
- remotion_1.Internals.validateFps(fps, 'as a prop of the <Player/> component');
64
+ remotion_1.Internals.validateFps(fps, 'as a prop of the <Player/> component', false);
65
+ (0, validate_in_out_frame_1.validateInOutFrames)({
66
+ durationInFrames,
67
+ inFrame,
68
+ outFrame,
69
+ });
63
70
  if (typeof controls !== 'boolean' && typeof controls !== 'undefined') {
64
71
  throw new TypeError(`'controls' must be a boolean or undefined but got '${typeof controls}' instead`);
65
72
  }
@@ -166,6 +173,8 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
166
173
  unregisterSequence: () => undefined,
167
174
  registerAsset: () => undefined,
168
175
  unregisterAsset: () => undefined,
176
+ currentCompositionMetadata: null,
177
+ setCurrentCompositionMetadata: () => undefined,
169
178
  assets: [],
170
179
  };
171
180
  }, [
@@ -179,9 +188,16 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
179
188
  const passedInputProps = (0, react_1.useMemo)(() => {
180
189
  return inputProps !== null && inputProps !== void 0 ? inputProps : {};
181
190
  }, [inputProps]);
182
- return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.TimelineContext.Provider, { value: timelineContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.SetTimelineContext.Provider, { value: setTimelineContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CompositionManager.Provider, { value: compositionManagerContext, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.MediaVolumeContext.Provider, { value: mediaVolumeContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.SetMediaVolumeContext.Provider, { value: setMediaVolumeContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.SharedAudioContextProvider, { numberOfAudioTags: numberOfSharedAudioTags, children: (0, jsx_runtime_1.jsx)(emitter_context_1.PlayerEventEmitterContext.Provider, { value: emitter, children: (0, jsx_runtime_1.jsx)(PlayerUI_1.default, { ref: rootRef, renderLoading: renderLoading, autoPlay: Boolean(autoPlay), loop: Boolean(loop), controls: Boolean(controls), errorFallback: errorFallback, style: style, inputProps: passedInputProps, allowFullscreen: Boolean(allowFullscreen), clickToPlay: typeof clickToPlay === 'boolean'
183
- ? clickToPlay
184
- : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), setMediaVolume: setMediaVolumeAndPersist, mediaVolume: mediaVolume, mediaMuted: mediaMuted, doubleClickToFullscreen: Boolean(doubleClickToFullscreen), setMediaMuted: setMediaMuted, spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: playbackRate }, void 0) }, void 0) }, void 0) }, void 0) }, void 0) }, void 0) }, void 0) }, void 0));
191
+ if (typeof window !== 'undefined') {
192
+ // eslint-disable-next-line react-hooks/rules-of-hooks
193
+ (0, react_1.useLayoutEffect)(() => {
194
+ // Inject CSS only on client, and also only after the Player has hydrated
195
+ remotion_1.Internals.CSSUtils.injectCSS(remotion_1.Internals.CSSUtils.makeDefaultCSS(`.${player_css_classname_1.PLAYER_CSS_CLASSNAME}`, '#fff'));
196
+ }, []);
197
+ }
198
+ return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.CanUseRemotionHooksProvider, { children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.TimelineContext.Provider, { value: timelineContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.SetTimelineContext.Provider, { value: setTimelineContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.CompositionManager.Provider, { value: compositionManagerContext, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.MediaVolumeContext.Provider, { value: mediaVolumeContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.SetMediaVolumeContext.Provider, { value: setMediaVolumeContextValue, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.SharedAudioContextProvider, { numberOfAudioTags: numberOfSharedAudioTags, children: (0, jsx_runtime_1.jsx)(emitter_context_1.PlayerEventEmitterContext.Provider, { value: emitter, children: (0, jsx_runtime_1.jsx)(PlayerUI_1.default, { ref: rootRef, renderLoading: renderLoading, autoPlay: Boolean(autoPlay), loop: Boolean(loop), controls: Boolean(controls), errorFallback: errorFallback, style: style, inputProps: passedInputProps, allowFullscreen: Boolean(allowFullscreen), moveToBeginningWhenEnded: Boolean(moveToBeginningWhenEnded), clickToPlay: typeof clickToPlay === 'boolean'
199
+ ? clickToPlay
200
+ : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), setMediaVolume: setMediaVolumeAndPersist, mediaVolume: mediaVolume, mediaMuted: mediaMuted, doubleClickToFullscreen: Boolean(doubleClickToFullscreen), setMediaMuted: setMediaMuted, spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: playbackRate, className: className !== null && className !== void 0 ? className : undefined, showPosterWhenUnplayed: Boolean(showPosterWhenUnplayed), showPosterWhenEnded: Boolean(showPosterWhenEnded), showPosterWhenPaused: Boolean(showPosterWhenPaused), renderPoster: renderPoster, inFrame: inFrame !== null && inFrame !== void 0 ? inFrame : null, outFrame: outFrame !== null && outFrame !== void 0 ? outFrame : null }) }) }) }) }) }) }) }) }));
185
201
  };
186
202
  exports.PlayerFn = PlayerFn;
187
203
  exports.Player = (0, react_1.forwardRef)(exports.PlayerFn);
@@ -1,5 +1,16 @@
1
- import React, { MouseEventHandler } from 'react';
2
- import { usePlayer } from './use-player';
1
+ import type { MouseEventHandler } from 'react';
2
+ import React from 'react';
3
+ import type { usePlayer } from './use-player';
4
+ declare global {
5
+ interface Document {
6
+ webkitFullscreenEnabled?: boolean;
7
+ webkitFullscreenElement?: Element;
8
+ webkitExitFullscreen?: Document['exitFullscreen'];
9
+ }
10
+ interface HTMLDivElement {
11
+ webkitRequestFullScreen: HTMLDivElement['requestFullscreen'];
12
+ }
13
+ }
3
14
  export declare const Controls: React.FC<{
4
15
  fps: number;
5
16
  durationInFrames: number;
@@ -11,4 +22,8 @@ export declare const Controls: React.FC<{
11
22
  allowFullscreen: boolean;
12
23
  onExitFullscreenButtonClick: MouseEventHandler<HTMLButtonElement>;
13
24
  spaceKeyToPlayOrPause: boolean;
25
+ onSeekEnd: () => void;
26
+ onSeekStart: () => void;
27
+ inFrame: number | null;
28
+ outFrame: number | null;
14
29
  }>;
@@ -8,7 +8,6 @@ const format_time_1 = require("./format-time");
8
8
  const icons_1 = require("./icons");
9
9
  const MediaVolumeSlider_1 = require("./MediaVolumeSlider");
10
10
  const PlayerSeekBar_1 = require("./PlayerSeekBar");
11
- const browser_supports_fullscreen_1 = require("./utils/browser-supports-fullscreen");
12
11
  const containerStyle = {
13
12
  boxSizing: 'border-box',
14
13
  position: 'absolute',
@@ -63,9 +62,10 @@ const timeLabel = {
63
62
  fontFamily: 'sans-serif',
64
63
  fontSize: 14,
65
64
  };
66
- const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, }) => {
65
+ const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, onSeekEnd, onSeekStart, inFrame, outFrame, }) => {
67
66
  const playButtonRef = (0, react_1.useRef)(null);
68
67
  const frame = remotion_1.Internals.Timeline.useTimelinePosition();
68
+ const [supportsFullscreen, setSupportsFullscreen] = (0, react_1.useState)(false);
69
69
  const containerCss = (0, react_1.useMemo)(() => {
70
70
  // Hide if playing and mouse outside
71
71
  const shouldShow = hovered || !player.playing;
@@ -77,11 +77,19 @@ const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVo
77
77
  (0, react_1.useEffect)(() => {
78
78
  if (playButtonRef.current && spaceKeyToPlayOrPause) {
79
79
  // This switches focus to play button when player.playing flag changes
80
- playButtonRef.current.focus();
80
+ playButtonRef.current.focus({
81
+ preventScroll: true,
82
+ });
81
83
  }
82
84
  }, [player.playing, spaceKeyToPlayOrPause]);
83
- return ((0, jsx_runtime_1.jsxs)("div", { style: containerCss, children: [(0, jsx_runtime_1.jsxs)("div", { style: controlsRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: leftPartStyle, children: [(0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, type: "button", style: buttonStyle, onClick: player.playing ? player.pause : player.play, "aria-label": player.playing ? 'Pause video' : 'Play video', title: player.playing ? 'Pause video' : 'Play video', children: player.playing ? (0, jsx_runtime_1.jsx)(icons_1.PauseIcon, {}, void 0) : (0, jsx_runtime_1.jsx)(icons_1.PlayIcon, {}, void 0) }, void 0), showVolumeControls ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: xSpacer }, void 0), (0, jsx_runtime_1.jsx)(MediaVolumeSlider_1.MediaVolumeSlider, {}, void 0)] }, void 0)) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }, void 0), (0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_1.formatTime)(frame / fps), " / ", (0, format_time_1.formatTime)(durationInFrames / fps)] }, void 0), (0, jsx_runtime_1.jsx)("div", { style: xSpacer }, void 0)] }, void 0), (0, jsx_runtime_1.jsx)("div", { style: flex1 }, void 0), browser_supports_fullscreen_1.browserSupportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("div", { style: fullscreen, children: (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', title: isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', style: buttonStyle, onClick: isFullscreen
85
+ (0, react_1.useEffect)(() => {
86
+ var _a;
87
+ // Must be handled client-side to avoid SSR hydration mismatch
88
+ setSupportsFullscreen((_a = (typeof document !== 'undefined' &&
89
+ (document.fullscreenEnabled || document.webkitFullscreenEnabled))) !== null && _a !== void 0 ? _a : false);
90
+ }, []);
91
+ return ((0, jsx_runtime_1.jsxs)("div", { style: containerCss, children: [(0, jsx_runtime_1.jsxs)("div", { style: controlsRow, children: [(0, jsx_runtime_1.jsxs)("div", { style: leftPartStyle, children: [(0, jsx_runtime_1.jsx)("button", { ref: playButtonRef, type: "button", style: buttonStyle, onClick: player.playing ? player.pause : player.play, "aria-label": player.playing ? 'Pause video' : 'Play video', title: player.playing ? 'Pause video' : 'Play video', children: player.playing ? (0, jsx_runtime_1.jsx)(icons_1.PauseIcon, {}) : (0, jsx_runtime_1.jsx)(icons_1.PlayIcon, {}) }), showVolumeControls ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsx)(MediaVolumeSlider_1.MediaVolumeSlider, {})] })) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_1.formatTime)(frame / fps), " / ", (0, format_time_1.formatTime)(durationInFrames / fps)] }), (0, jsx_runtime_1.jsx)("div", { style: xSpacer })] }), (0, jsx_runtime_1.jsx)("div", { style: flex1 }), (0, jsx_runtime_1.jsx)("div", { style: fullscreen, children: supportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', title: isFullscreen ? 'Exit fullscreen' : 'Enter Fullscreen', style: buttonStyle, onClick: isFullscreen
84
92
  ? onExitFullscreenButtonClick
85
- : onFullscreenButtonClick, children: (0, jsx_runtime_1.jsx)(icons_1.FullscreenIcon, { minimized: !isFullscreen }, void 0) }, void 0) }, void 0)) : null] }, void 0), (0, jsx_runtime_1.jsx)("div", { style: ySpacer }, void 0), (0, jsx_runtime_1.jsx)(PlayerSeekBar_1.PlayerSeekBar, { durationInFrames: durationInFrames }, void 0)] }, void 0));
93
+ : onFullscreenButtonClick, children: (0, jsx_runtime_1.jsx)(icons_1.FullscreenIcon, { minimized: !isFullscreen }) })) : null })] }), (0, jsx_runtime_1.jsx)("div", { style: ySpacer }), (0, jsx_runtime_1.jsx)(PlayerSeekBar_1.PlayerSeekBar, { onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, durationInFrames: durationInFrames, inFrame: inFrame, outFrame: outFrame })] }));
86
94
  };
87
95
  exports.Controls = Controls;
@@ -1,4 +1,8 @@
1
1
  /// <reference types="react" />
2
2
  export declare const PlayerSeekBar: React.FC<{
3
3
  durationInFrames: number;
4
+ onSeekStart: () => void;
5
+ onSeekEnd: () => void;
6
+ inFrame: number | null;
7
+ outFrame: number | null;
4
8
  }>;
@@ -30,11 +30,18 @@ const containerStyle = {
30
30
  };
31
31
  const barBackground = {
32
32
  height: BAR_HEIGHT,
33
- backgroundColor: 'rgba(255, 255, 255, 0.5)',
33
+ backgroundColor: 'rgba(255, 255, 255, 0.25)',
34
34
  width: '100%',
35
35
  borderRadius: BAR_HEIGHT / 2,
36
36
  };
37
- const PlayerSeekBar = ({ durationInFrames }) => {
37
+ const findBodyInWhichDivIsLocated = (div) => {
38
+ let current = div;
39
+ while (current.parentElement) {
40
+ current = current.parentElement;
41
+ }
42
+ return current;
43
+ };
44
+ const PlayerSeekBar = ({ durationInFrames, onSeekEnd, onSeekStart, inFrame, outFrame }) => {
38
45
  const containerRef = (0, react_1.useRef)(null);
39
46
  const barHovered = (0, use_hover_state_1.useHoverState)(containerRef);
40
47
  const size = (0, use_element_size_1.useElementSize)(containerRef, {
@@ -57,7 +64,8 @@ const PlayerSeekBar = ({ durationInFrames }) => {
57
64
  dragging: true,
58
65
  wasPlaying: playing,
59
66
  });
60
- }, [size, durationInFrames, pause, seek, playing]);
67
+ onSeekStart();
68
+ }, [size, durationInFrames, pause, seek, playing, onSeekStart]);
61
69
  const onPointerMove = (0, react_1.useCallback)((e) => {
62
70
  var _a;
63
71
  if (!size) {
@@ -82,16 +90,18 @@ const PlayerSeekBar = ({ durationInFrames }) => {
82
90
  else {
83
91
  pause();
84
92
  }
85
- }, [dragging, pause, play]);
93
+ onSeekEnd();
94
+ }, [dragging, onSeekEnd, pause, play]);
86
95
  (0, react_1.useEffect)(() => {
87
96
  if (!dragging.dragging) {
88
97
  return;
89
98
  }
90
- window.addEventListener('pointermove', onPointerMove);
91
- window.addEventListener('pointerup', onPointerUp);
99
+ const body = findBodyInWhichDivIsLocated(containerRef.current);
100
+ body.addEventListener('pointermove', onPointerMove);
101
+ body.addEventListener('pointerup', onPointerUp);
92
102
  return () => {
93
- window.removeEventListener('pointermove', onPointerMove);
94
- window.removeEventListener('pointerup', onPointerUp);
103
+ body.removeEventListener('pointermove', onPointerMove);
104
+ body.removeEventListener('pointerup', onPointerUp);
95
105
  };
96
106
  }, [dragging.dragging, onPointerMove, onPointerUp]);
97
107
  const knobStyle = (0, react_1.useMemo)(() => {
@@ -113,10 +123,24 @@ const PlayerSeekBar = ({ durationInFrames }) => {
113
123
  return {
114
124
  height: BAR_HEIGHT,
115
125
  backgroundColor: 'rgba(255, 255, 255, 1)',
116
- width: (frame / (durationInFrames - 1)) * 100 + '%',
126
+ width: ((frame - (inFrame !== null && inFrame !== void 0 ? inFrame : 0)) / (durationInFrames - 1)) * 100 + '%',
127
+ marginLeft: ((inFrame !== null && inFrame !== void 0 ? inFrame : 0) / (durationInFrames - 1)) * 100 + '%',
128
+ borderRadius: BAR_HEIGHT / 2,
129
+ };
130
+ }, [durationInFrames, frame, inFrame]);
131
+ const active = (0, react_1.useMemo)(() => {
132
+ return {
133
+ height: BAR_HEIGHT,
134
+ backgroundColor: 'rgba(255, 255, 255, 0.25)',
135
+ width: (((outFrame !== null && outFrame !== void 0 ? outFrame : durationInFrames - 1) - (inFrame !== null && inFrame !== void 0 ? inFrame : 0)) /
136
+ (durationInFrames - 1)) *
137
+ 100 +
138
+ '%',
139
+ marginLeft: ((inFrame !== null && inFrame !== void 0 ? inFrame : 0) / (durationInFrames - 1)) * 100 + '%',
117
140
  borderRadius: BAR_HEIGHT / 2,
141
+ position: 'absolute',
118
142
  };
119
- }, [durationInFrames, frame]);
120
- return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, onPointerDown: onPointerDown, style: containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { style: barBackground, children: (0, jsx_runtime_1.jsx)("div", { style: fillStyle }, void 0) }, void 0), (0, jsx_runtime_1.jsx)("div", { style: knobStyle }, void 0)] }, void 0));
143
+ }, [durationInFrames, inFrame, outFrame]);
144
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, onPointerDown: onPointerDown, style: containerStyle, children: [(0, jsx_runtime_1.jsxs)("div", { style: barBackground, children: [(0, jsx_runtime_1.jsx)("div", { style: active }), (0, jsx_runtime_1.jsx)("div", { style: fillStyle })] }), (0, jsx_runtime_1.jsx)("div", { style: knobStyle })] }));
121
145
  };
122
146
  exports.PlayerSeekBar = PlayerSeekBar;
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { PlayerRef } from './player-methods';
2
+ import type { PlayerRef } from './player-methods';
3
3
  export declare type ErrorFallback = (info: {
4
4
  error: Error;
5
5
  }) => React.ReactNode;
@@ -7,6 +7,7 @@ export declare type RenderLoading = (canvas: {
7
7
  height: number;
8
8
  width: number;
9
9
  }) => React.ReactChild;
10
+ export declare type RenderPoster = RenderLoading;
10
11
  declare const _default: (props: {
11
12
  controls: boolean;
12
13
  loop: boolean;
@@ -24,6 +25,14 @@ declare const _default: (props: {
24
25
  mediaVolume: number;
25
26
  errorFallback: ErrorFallback;
26
27
  playbackRate: number;
27
- renderLoading?: RenderLoading | undefined;
28
+ renderLoading: RenderLoading | undefined;
29
+ renderPoster: RenderLoading | undefined;
30
+ className: string | undefined;
31
+ moveToBeginningWhenEnded: boolean;
32
+ showPosterWhenPaused: boolean;
33
+ showPosterWhenEnded: boolean;
34
+ showPosterWhenUnplayed: boolean;
35
+ inFrame: number | null;
36
+ outFrame: number | null;
28
37
  } & React.RefAttributes<PlayerRef | null>) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
29
38
  export default _default;
package/dist/PlayerUI.js CHANGED
@@ -1,7 +1,30 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  const jsx_runtime_1 = require("react/jsx-runtime");
4
- const react_1 = require("react");
27
+ const react_1 = __importStar(require("react"));
5
28
  const remotion_1 = require("remotion");
6
29
  const calculate_scale_1 = require("./calculate-scale");
7
30
  const error_boundary_1 = require("./error-boundary");
@@ -10,12 +33,16 @@ const PlayerControls_1 = require("./PlayerControls");
10
33
  const use_hover_state_1 = require("./use-hover-state");
11
34
  const use_playback_1 = require("./use-playback");
12
35
  const use_player_1 = require("./use-player");
13
- const browser_supports_fullscreen_1 = require("./utils/browser-supports-fullscreen");
14
36
  const calculate_player_size_1 = require("./utils/calculate-player-size");
15
37
  const is_node_1 = require("./utils/is-node");
16
38
  const use_click_prevention_on_double_click_1 = require("./utils/use-click-prevention-on-double-click");
17
39
  const use_element_size_1 = require("./utils/use-element-size");
18
- const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, mediaVolume, mediaMuted, doubleClickToFullscreen, setMediaMuted, setMediaVolume, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, }, ref) => {
40
+ const reactVersion = react_1.default.version.split('.')[0];
41
+ if (reactVersion === '0') {
42
+ throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
43
+ }
44
+ const doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
45
+ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, mediaVolume, mediaMuted, doubleClickToFullscreen, setMediaMuted, setMediaVolume, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, inFrame, outFrame, }, ref) => {
19
46
  var _a, _b, _c;
20
47
  const config = remotion_1.Internals.useUnsafeVideoConfig();
21
48
  const video = remotion_1.Internals.useVideo();
@@ -28,7 +55,14 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
28
55
  const [hasPausedToResume, setHasPausedToResume] = (0, react_1.useState)(false);
29
56
  const [shouldAutoplay, setShouldAutoPlay] = (0, react_1.useState)(autoPlay);
30
57
  const [isFullscreen, setIsFullscreen] = (0, react_1.useState)(() => false);
31
- (0, use_playback_1.usePlayback)({ loop, playbackRate });
58
+ const [seeking, setSeeking] = (0, react_1.useState)(false);
59
+ (0, use_playback_1.usePlayback)({
60
+ loop,
61
+ playbackRate,
62
+ moveToBeginningWhenEnded,
63
+ inFrame,
64
+ outFrame,
65
+ });
32
66
  const player = (0, use_player_1.usePlayer)();
33
67
  (0, react_1.useEffect)(() => {
34
68
  if (hasPausedToResume && !player.playing) {
@@ -64,7 +98,8 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
64
98
  if (!allowFullscreen) {
65
99
  throw new Error('allowFullscreen is false');
66
100
  }
67
- if (!browser_supports_fullscreen_1.browserSupportsFullscreen) {
101
+ const supportsFullScreen = document.fullscreenEnabled || document.webkitFullscreenEnabled;
102
+ if (!supportsFullScreen) {
68
103
  throw new Error('Browser doesnt support fullscreen');
69
104
  }
70
105
  if (!container.current) {
@@ -85,6 +120,32 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
85
120
  document.exitFullscreen();
86
121
  }
87
122
  }, []);
123
+ (0, react_1.useEffect)(() => {
124
+ const { current } = container;
125
+ if (!current) {
126
+ return;
127
+ }
128
+ const fullscreenChange = () => {
129
+ var _a;
130
+ const element = (_a = document.webkitFullscreenElement) !== null && _a !== void 0 ? _a : document.fullscreenElement;
131
+ if (element && element === container.current) {
132
+ player.emitter.dispatchFullscreenChangeUpdate({
133
+ isFullscreen: true,
134
+ });
135
+ }
136
+ else {
137
+ player.emitter.dispatchFullscreenChangeUpdate({
138
+ isFullscreen: false,
139
+ });
140
+ }
141
+ };
142
+ current.addEventListener('webkitfullscreenchange', fullscreenChange);
143
+ current.addEventListener('fullscreenchange', fullscreenChange);
144
+ return () => {
145
+ current.removeEventListener('webkitfullscreenchange', fullscreenChange);
146
+ current.removeEventListener('fullscreenchange', fullscreenChange);
147
+ };
148
+ }, [player.emitter]);
88
149
  const durationInFrames = (_a = config === null || config === void 0 ? void 0 : config.durationInFrames) !== null && _a !== void 0 ? _a : 1;
89
150
  (0, react_1.useImperativeHandle)(ref, () => {
90
151
  const methods = {
@@ -172,7 +233,7 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
172
233
  if (!config || !canvasSize) {
173
234
  return null;
174
235
  }
175
- return (0, calculate_scale_1.calculateScale)({
236
+ return (0, calculate_scale_1.calculateCanvasTransformation)({
176
237
  canvasSize,
177
238
  compositionHeight: config.height,
178
239
  compositionWidth: config.width,
@@ -199,7 +260,7 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
199
260
  if (!config || !canvasSize) {
200
261
  return {};
201
262
  }
202
- const { scale, xCorrection, yCorrection } = (0, calculate_scale_1.calculateScale)({
263
+ const { scale, xCorrection, yCorrection } = (0, calculate_scale_1.calculateCanvasTransformation)({
203
264
  canvasSize,
204
265
  compositionHeight: config.height,
205
266
  compositionWidth: config.width,
@@ -232,6 +293,12 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
232
293
  const onSingleClick = (0, react_1.useCallback)((e) => {
233
294
  toggle(e);
234
295
  }, [toggle]);
296
+ const onSeekStart = (0, react_1.useCallback)(() => {
297
+ setSeeking(true);
298
+ }, []);
299
+ const onSeekEnd = (0, react_1.useCallback)(() => {
300
+ setSeeking(false);
301
+ }, []);
235
302
  const onDoubleClick = (0, react_1.useCallback)(() => {
236
303
  if (isFullscreen) {
237
304
  exitFullscreen();
@@ -250,10 +317,24 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
250
317
  if (!config) {
251
318
  return null;
252
319
  }
253
- const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: outer, onClick: clickToPlay ? handleClick : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: (0, jsx_runtime_1.jsx)("div", { style: containerStyle, className: player_css_classname_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_b = video === null || video === void 0 ? void 0 : video.defaultProps) !== null && _b !== void 0 ? _b : {}), ...((_c = inputProps) !== null && _c !== void 0 ? _c : {}) }, void 0) }, void 0)) : null }, void 0) }, void 0), controls ? ((0, jsx_runtime_1.jsx)(PlayerControls_1.Controls, { fps: config.fps, durationInFrames: config.durationInFrames, hovered: hovered, player: player, onFullscreenButtonClick: onFullscreenButtonClick, isFullscreen: isFullscreen, allowFullscreen: allowFullscreen, showVolumeControls: showVolumeControls, onExitFullscreenButtonClick: onExitFullscreenButtonClick, spaceKeyToPlayOrPause: spaceKeyToPlayOrPause }, void 0)) : null] }, void 0));
254
- // Don't render suspense on Node.js
255
- if (is_node_1.IS_NODE) {
256
- return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, children: content }, void 0));
320
+ const poster = renderPoster
321
+ ? renderPoster({
322
+ height: outerStyle.height,
323
+ width: outerStyle.width,
324
+ })
325
+ : null;
326
+ if (poster === undefined) {
327
+ throw new TypeError('renderPoster() must return a React element, but undefined was returned');
328
+ }
329
+ const shouldShowPoster = poster &&
330
+ [
331
+ showPosterWhenPaused && !player.isPlaying() && !seeking,
332
+ showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
333
+ showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
334
+ ].some(Boolean);
335
+ const content = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { style: outer, onClick: clickToPlay ? handleClick : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: (0, jsx_runtime_1.jsx)("div", { style: containerStyle, className: player_css_classname_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_b = video === null || video === void 0 ? void 0 : video.defaultProps) !== null && _b !== void 0 ? _b : {}), ...((_c = inputProps) !== null && _c !== void 0 ? _c : {}) }) })) : null }) }), shouldShowPoster ? ((0, jsx_runtime_1.jsx)("div", { style: outer, onClick: clickToPlay ? handleClick : undefined, onDoubleClick: doubleClickToFullscreen ? handleDoubleClick : undefined, children: poster })) : null, controls ? ((0, jsx_runtime_1.jsx)(PlayerControls_1.Controls, { fps: config.fps, durationInFrames: config.durationInFrames, hovered: hovered, player: player, onFullscreenButtonClick: onFullscreenButtonClick, isFullscreen: isFullscreen, allowFullscreen: allowFullscreen, showVolumeControls: showVolumeControls, onExitFullscreenButtonClick: onExitFullscreenButtonClick, spaceKeyToPlayOrPause: spaceKeyToPlayOrPause, onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, inFrame: inFrame, outFrame: outFrame })) : null] }));
336
+ if (is_node_1.IS_NODE && !doesReactVersionSupportSuspense) {
337
+ return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, className: className, children: content }));
257
338
  }
258
339
  const loadingMarkup = renderLoading
259
340
  ? renderLoading({
@@ -261,6 +342,6 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
261
342
  width: outerStyle.width,
262
343
  })
263
344
  : null;
264
- return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, children: (0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: loadingMarkup, children: content }, void 0) }, void 0));
345
+ return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, className: className, children: (0, jsx_runtime_1.jsx)(react_1.Suspense, { fallback: loadingMarkup, children: content }) }));
265
346
  };
266
347
  exports.default = (0, react_1.forwardRef)(PlayerUI);
@@ -1,7 +1,13 @@
1
- import { PreviewSize } from './utils/preview-size';
2
- import { Size } from './utils/use-element-size';
3
- export declare const calculateScale: ({ previewSize, compositionWidth, compositionHeight, canvasSize, }: {
4
- previewSize: PreviewSize;
1
+ import type { PreviewSize } from './utils/preview-size';
2
+ import type { Size } from './utils/use-element-size';
3
+ export declare const calculateScale: ({ canvasSize, compositionHeight, compositionWidth, previewSize, }: {
4
+ previewSize: PreviewSize['size'];
5
+ compositionWidth: number;
6
+ compositionHeight: number;
7
+ canvasSize: Size;
8
+ }) => number;
9
+ export declare const calculateCanvasTransformation: ({ previewSize, compositionWidth, compositionHeight, canvasSize, }: {
10
+ previewSize: PreviewSize['size'];
5
11
  compositionWidth: number;
6
12
  compositionHeight: number;
7
13
  canvasSize: Size;
@@ -1,11 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calculateScale = void 0;
4
- const calculateScale = ({ previewSize, compositionWidth, compositionHeight, canvasSize, }) => {
3
+ exports.calculateCanvasTransformation = exports.calculateScale = void 0;
4
+ const calculateScale = ({ canvasSize, compositionHeight, compositionWidth, previewSize, }) => {
5
5
  const heightRatio = canvasSize.height / compositionHeight;
6
6
  const widthRatio = canvasSize.width / compositionWidth;
7
7
  const ratio = Math.min(heightRatio, widthRatio);
8
- const scale = previewSize === 'auto' ? ratio : Number(previewSize);
8
+ return previewSize === 'auto' ? ratio : Number(previewSize);
9
+ };
10
+ exports.calculateScale = calculateScale;
11
+ const calculateCanvasTransformation = ({ previewSize, compositionWidth, compositionHeight, canvasSize, }) => {
12
+ const scale = (0, exports.calculateScale)({
13
+ canvasSize,
14
+ compositionHeight,
15
+ compositionWidth,
16
+ previewSize,
17
+ });
9
18
  const correction = 0 - (1 - scale) / 2;
10
19
  const xCorrection = correction * compositionWidth;
11
20
  const yCorrection = correction * compositionHeight;
@@ -21,4 +30,4 @@ const calculateScale = ({ previewSize, compositionWidth, compositionHeight, canv
21
30
  scale,
22
31
  };
23
32
  };
24
- exports.calculateScale = calculateScale;
33
+ exports.calculateCanvasTransformation = calculateCanvasTransformation;
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
- import { PlayerEmitter } from './event-emitter';
2
+ import type { PlayerEmitter } from './event-emitter';
3
3
  export declare const PlayerEventEmitterContext: React.Context<PlayerEmitter | undefined>;
@@ -31,7 +31,7 @@ class ErrorBoundary extends react_1.default.Component {
31
31
  // You can render any custom fallback UI
32
32
  return ((0, jsx_runtime_1.jsx)("div", { style: errorStyle, children: this.props.errorFallback({
33
33
  error: this.state.hasError,
34
- }) }, void 0));
34
+ }) }));
35
35
  }
36
36
  return this.props.children;
37
37
  }
@@ -10,6 +10,9 @@ declare type TimeUpdateEventPayload = {
10
10
  declare type RateChangeEventPayload = {
11
11
  playbackRate: number;
12
12
  };
13
+ declare type FullscreenChangeEventPayload = {
14
+ isFullscreen: boolean;
15
+ };
13
16
  declare type StateEventMap = {
14
17
  seeked: SeekPayload;
15
18
  pause: undefined;
@@ -18,6 +21,7 @@ declare type StateEventMap = {
18
21
  ended: undefined;
19
22
  error: ErrorPayload;
20
23
  timeupdate: TimeUpdateEventPayload;
24
+ fullscreenchange: FullscreenChangeEventPayload;
21
25
  };
22
26
  export declare type EventTypes = keyof StateEventMap;
23
27
  export declare type CallbackListener<T extends EventTypes> = (data: {
@@ -38,5 +42,6 @@ export declare class PlayerEmitter {
38
42
  dispatchRatechange(playbackRate: number): void;
39
43
  dispatchError(error: Error): void;
40
44
  dispatchTimeUpdate(event: TimeUpdateEventPayload): void;
45
+ dispatchFullscreenChangeUpdate(event: FullscreenChangeEventPayload): void;
41
46
  }
42
47
  export {};
@@ -11,6 +11,7 @@ class PlayerEmitter {
11
11
  ratechange: [],
12
12
  seeked: [],
13
13
  timeupdate: [],
14
+ fullscreenchange: [],
14
15
  };
15
16
  }
16
17
  addEventListener(name, callback) {
@@ -51,5 +52,8 @@ class PlayerEmitter {
51
52
  dispatchTimeUpdate(event) {
52
53
  this.dispatchEvent('timeupdate', event);
53
54
  }
55
+ dispatchFullscreenChangeUpdate(event) {
56
+ this.dispatchEvent('fullscreenchange', event);
57
+ }
54
58
  }
55
59
  exports.PlayerEmitter = PlayerEmitter;