@remotion/player 4.0.0-alpha10 → 4.0.0-alpha11

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.
@@ -70,7 +70,6 @@ const MediaVolumeSlider = ({ displayVerticalVolumeSlider }) => {
70
70
  display: 'inline-flex',
71
71
  background: 'none',
72
72
  border: 'none',
73
- padding: 6,
74
73
  justifyContent: 'center',
75
74
  alignItems: 'center',
76
75
  touchAction: 'none',
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export declare const Checkmark: () => JSX.Element;
3
+ export declare const PlaybackrateControl: React.FC<{
4
+ playbackRates: number[];
5
+ }>;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PlaybackrateControl = exports.Checkmark = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = require("react");
9
+ const remotion_1 = require("remotion");
10
+ const icons_js_1 = require("./icons.js");
11
+ const use_component_visible_js_1 = __importDefault(require("./utils/use-component-visible.js"));
12
+ const playbackPopup = {
13
+ position: 'absolute',
14
+ right: 0,
15
+ width: 125,
16
+ bottom: 35,
17
+ background: '#fff',
18
+ borderRadius: 4,
19
+ overflow: 'hidden',
20
+ color: 'black',
21
+ textAlign: 'left',
22
+ };
23
+ const rateDiv = {
24
+ height: 30,
25
+ paddingRight: 15,
26
+ paddingLeft: 12,
27
+ display: 'flex',
28
+ flexDirection: 'row',
29
+ alignItems: 'center',
30
+ };
31
+ const checkmarkContainer = {
32
+ width: 22,
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ };
36
+ const checkmarkStyle = {
37
+ width: 14,
38
+ height: 14,
39
+ color: 'black',
40
+ };
41
+ const Checkmark = () => ((0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 512 512", style: checkmarkStyle, children: (0, jsx_runtime_1.jsx)("path", { fill: "currentColor", d: "M435.848 83.466L172.804 346.51l-96.652-96.652c-4.686-4.686-12.284-4.686-16.971 0l-28.284 28.284c-4.686 4.686-4.686 12.284 0 16.971l133.421 133.421c4.686 4.686 12.284 4.686 16.971 0l299.813-299.813c4.686-4.686 4.686-12.284 0-16.971l-28.284-28.284c-4.686-4.686-12.284-4.686-16.97 0z" }) }));
42
+ exports.Checkmark = Checkmark;
43
+ const PlaybackPopup = ({ setIsComponentVisible, playbackRates }) => {
44
+ const { setPlaybackRate, playbackRate } = (0, react_1.useContext)(remotion_1.Internals.Timeline.TimelineContext);
45
+ const [keyboardSelectedRate, setKeyboardSelectedRate] = (0, react_1.useState)(playbackRate);
46
+ (0, react_1.useEffect)(() => {
47
+ const listener = (e) => {
48
+ e.preventDefault();
49
+ if (e.key === 'ArrowUp') {
50
+ const currentIndex = playbackRates.findIndex((rate) => rate === keyboardSelectedRate);
51
+ if (currentIndex === 0) {
52
+ return;
53
+ }
54
+ if (currentIndex === -1) {
55
+ setKeyboardSelectedRate(playbackRates[0]);
56
+ }
57
+ else {
58
+ setKeyboardSelectedRate(playbackRates[currentIndex - 1]);
59
+ }
60
+ }
61
+ else if (e.key === 'ArrowDown') {
62
+ const currentIndex = playbackRates.findIndex((rate) => rate === keyboardSelectedRate);
63
+ if (currentIndex === playbackRates.length - 1) {
64
+ return;
65
+ }
66
+ if (currentIndex === -1) {
67
+ setKeyboardSelectedRate(playbackRates[playbackRates.length - 1]);
68
+ }
69
+ else {
70
+ setKeyboardSelectedRate(playbackRates[currentIndex + 1]);
71
+ }
72
+ }
73
+ else if (e.key === 'Enter') {
74
+ setPlaybackRate(keyboardSelectedRate);
75
+ setIsComponentVisible(false);
76
+ }
77
+ };
78
+ window.addEventListener('keydown', listener);
79
+ return () => {
80
+ window.removeEventListener('keydown', listener);
81
+ };
82
+ }, [
83
+ playbackRates,
84
+ keyboardSelectedRate,
85
+ setPlaybackRate,
86
+ setIsComponentVisible,
87
+ ]);
88
+ const onSelect = (0, react_1.useCallback)((rate) => {
89
+ setPlaybackRate(rate);
90
+ setIsComponentVisible(false);
91
+ }, [setIsComponentVisible, setPlaybackRate]);
92
+ return ((0, jsx_runtime_1.jsx)("div", { style: playbackPopup, children: playbackRates.map((rate) => {
93
+ return ((0, jsx_runtime_1.jsx)(PlaybackrateOption, { selectedRate: playbackRate, onSelect: onSelect, rate: rate, keyboardSelectedRate: keyboardSelectedRate }, rate));
94
+ }) }));
95
+ };
96
+ const PlaybackrateOption = ({ rate, onSelect, selectedRate, keyboardSelectedRate }) => {
97
+ const onClick = (0, react_1.useCallback)((e) => {
98
+ e.stopPropagation();
99
+ e.preventDefault();
100
+ onSelect(rate);
101
+ }, [onSelect, rate]);
102
+ const [hovered, setHovered] = (0, react_1.useState)(false);
103
+ const onMouseEnter = (0, react_1.useCallback)(() => {
104
+ setHovered(true);
105
+ }, []);
106
+ const onMouseLeave = (0, react_1.useCallback)(() => {
107
+ setHovered(false);
108
+ }, []);
109
+ const actualStyle = (0, react_1.useMemo)(() => {
110
+ return {
111
+ ...rateDiv,
112
+ backgroundColor: hovered || keyboardSelectedRate === rate ? '#eee' : 'transparent',
113
+ };
114
+ }, [hovered, keyboardSelectedRate, rate]);
115
+ return ((0, jsx_runtime_1.jsxs)("div", { onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, tabIndex: 0, style: actualStyle, onClick: onClick, children: [(0, jsx_runtime_1.jsx)("div", { style: checkmarkContainer, children: rate === selectedRate ? (0, jsx_runtime_1.jsx)(exports.Checkmark, {}) : null }), rate.toFixed(1), "x"] }, rate));
116
+ };
117
+ const playbackButton = {
118
+ position: 'relative',
119
+ display: 'inline-flex',
120
+ alignItems: 'center',
121
+ padding: '6px 0 6px 0',
122
+ border: 'none',
123
+ background: 'none',
124
+ height: 36,
125
+ cursor: 'pointer',
126
+ };
127
+ const PlaybackrateControl = ({ playbackRates }) => {
128
+ const { ref, isComponentVisible, setIsComponentVisible } = (0, use_component_visible_js_1.default)(false);
129
+ const onClick = (0, react_1.useCallback)((e) => {
130
+ e.stopPropagation();
131
+ e.preventDefault();
132
+ setIsComponentVisible(!isComponentVisible);
133
+ }, [isComponentVisible, setIsComponentVisible]);
134
+ return ((0, jsx_runtime_1.jsx)("div", { ref: ref, children: (0, jsx_runtime_1.jsxs)("button", { type: "button", "aria-label": "Change playback rate", style: playbackButton, onClick: onClick, children: [(0, jsx_runtime_1.jsx)(icons_js_1.SettingsIcon, { iconSize: 22 }), isComponentVisible && ((0, jsx_runtime_1.jsx)(PlaybackPopup, { playbackRates: playbackRates, setIsComponentVisible: setIsComponentVisible }))] }) }));
135
+ };
136
+ exports.PlaybackrateControl = PlaybackrateControl;
@@ -42,6 +42,7 @@ export declare type PlayerProps<Schema extends AnyZodObject, Props> = {
42
42
  alwaysShowControls?: boolean;
43
43
  schema?: Schema;
44
44
  initiallyMuted?: boolean;
45
+ showPlaybackRateControl?: boolean | number[];
45
46
  } & CompProps<Props> & PropsIfHasProps<Schema, Props>;
46
47
  export declare const componentOrNullIfLazy: <Props>(props: CompProps<Props>) => ComponentType<Props> | null;
47
48
  /**
@@ -22,7 +22,7 @@ const componentOrNullIfLazy = (props) => {
22
22
  return null;
23
23
  };
24
24
  exports.componentOrNullIfLazy = componentOrNullIfLazy;
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, initiallyShowControls, renderFullscreenButton, renderPlayPauseButton, alwaysShowControls = false, initiallyMuted = false, ...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, initiallyShowControls, renderFullscreenButton, renderPlayPauseButton, alwaysShowControls = false, initiallyMuted = false, showPlaybackRateControl = false, ...componentProps }, ref) => {
26
26
  if (typeof window !== 'undefined') {
27
27
  // eslint-disable-next-line react-hooks/rules-of-hooks
28
28
  (0, react_1.useLayoutEffect)(() => {
@@ -50,6 +50,7 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
50
50
  const rootRef = (0, react_1.useRef)(null);
51
51
  const audioAndVideoTags = (0, react_1.useRef)([]);
52
52
  const imperativePlaying = (0, react_1.useRef)(false);
53
+ const [currentPlaybackRate, setCurrentPlaybackRate] = (0, react_1.useState)(playbackRate);
53
54
  if (typeof compositionHeight !== 'number') {
54
55
  throw new TypeError(`'compositionHeight' must be a number but got '${typeof compositionHeight}' instead`);
55
56
  }
@@ -105,10 +106,13 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
105
106
  numberOfSharedAudioTags < 0) {
106
107
  throw new TypeError(`'numberOfSharedAudioTags' must be an integer but got '${numberOfSharedAudioTags}' instead`);
107
108
  }
108
- (0, validate_playbackrate_js_1.validatePlaybackRate)(playbackRate);
109
+ (0, validate_playbackrate_js_1.validatePlaybackRate)(currentPlaybackRate);
109
110
  (0, react_1.useEffect)(() => {
110
- emitter.dispatchRateChange(playbackRate);
111
- }, [emitter, playbackRate]);
111
+ emitter.dispatchRateChange(currentPlaybackRate);
112
+ }, [emitter, currentPlaybackRate]);
113
+ (0, react_1.useEffect)(() => {
114
+ setCurrentPlaybackRate(playbackRate);
115
+ }, [playbackRate]);
112
116
  (0, react_1.useImperativeHandle)(ref, () => rootRef.current, []);
113
117
  const timelineContextValue = (0, react_1.useMemo)(() => {
114
118
  return {
@@ -116,14 +120,14 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
116
120
  playing,
117
121
  rootId,
118
122
  shouldRegisterSequences: false,
119
- playbackRate,
123
+ playbackRate: currentPlaybackRate,
120
124
  imperativePlaying,
121
- setPlaybackRate: () => {
122
- throw new Error('playback rate');
125
+ setPlaybackRate: (rate) => {
126
+ setCurrentPlaybackRate(rate);
123
127
  },
124
128
  audioAndVideoTags,
125
129
  };
126
- }, [frame, playbackRate, playing, rootId]);
130
+ }, [frame, currentPlaybackRate, playing, rootId]);
127
131
  const setTimelineContextValue = (0, react_1.useMemo)(() => {
128
132
  return {
129
133
  setFrame,
@@ -143,7 +147,7 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
143
147
  const actualInputProps = (0, react_1.useMemo)(() => inputProps !== null && inputProps !== void 0 ? inputProps : {}, [inputProps]);
144
148
  return ((0, jsx_runtime_1.jsx)(remotion_1.Internals.IsPlayerContextProvider, { children: (0, jsx_runtime_1.jsx)(SharedPlayerContext_js_1.SharedPlayerContexts, { timelineContext: timelineContextValue, component: component, compositionHeight: compositionHeight, compositionWidth: compositionWidth, durationInFrames: durationInFrames, fps: fps, inputProps: actualInputProps, numberOfSharedAudioTags: numberOfSharedAudioTags, initiallyMuted: initiallyMuted, children: (0, jsx_runtime_1.jsx)(remotion_1.Internals.Timeline.SetTimelineContext.Provider, { value: setTimelineContextValue, children: (0, jsx_runtime_1.jsx)(emitter_context_js_1.PlayerEventEmitterContext.Provider, { value: emitter, children: (0, jsx_runtime_1.jsx)(PlayerUI_js_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'
145
149
  ? clickToPlay
146
- : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), doubleClickToFullscreen: Boolean(doubleClickToFullscreen), 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, initiallyShowControls: initiallyShowControls !== null && initiallyShowControls !== void 0 ? initiallyShowControls : true, renderFullscreen: renderFullscreenButton !== null && renderFullscreenButton !== void 0 ? renderFullscreenButton : null, renderPlayPauseButton: renderPlayPauseButton !== null && renderPlayPauseButton !== void 0 ? renderPlayPauseButton : null, alwaysShowControls: alwaysShowControls }) }) }) }) }));
150
+ : Boolean(controls), showVolumeControls: Boolean(showVolumeControls), doubleClickToFullscreen: Boolean(doubleClickToFullscreen), spaceKeyToPlayOrPause: Boolean(spaceKeyToPlayOrPause), playbackRate: currentPlaybackRate, 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, initiallyShowControls: initiallyShowControls !== null && initiallyShowControls !== void 0 ? initiallyShowControls : true, renderFullscreen: renderFullscreenButton !== null && renderFullscreenButton !== void 0 ? renderFullscreenButton : null, renderPlayPauseButton: renderPlayPauseButton !== null && renderPlayPauseButton !== void 0 ? renderPlayPauseButton : null, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl }) }) }) }) }));
147
151
  };
148
152
  const forward = react_1.forwardRef;
149
153
  /**
@@ -37,4 +37,5 @@ export declare const Controls: React.FC<{
37
37
  renderPlayPauseButton: RenderPlayPauseButton | null;
38
38
  renderFullscreenButton: RenderFullscreenButton | null;
39
39
  alwaysShowControls: boolean;
40
+ showPlaybackRateControl: boolean | number[];
40
41
  }>;
@@ -7,6 +7,7 @@ const remotion_1 = require("remotion");
7
7
  const format_time_js_1 = require("./format-time.js");
8
8
  const icons_js_1 = require("./icons.js");
9
9
  const MediaVolumeSlider_js_1 = require("./MediaVolumeSlider.js");
10
+ const PlaybackrateControl_js_1 = require("./PlaybackrateControl.js");
10
11
  const PlayerSeekBar_js_1 = require("./PlayerSeekBar.js");
11
12
  const use_video_controls_resize_js_1 = require("./use-video-controls-resize.js");
12
13
  const gradientSteps = [
@@ -66,7 +67,7 @@ const leftPartStyle = {
66
67
  alignItems: 'center',
67
68
  };
68
69
  const xSpacer = {
69
- width: 10,
70
+ width: 12,
70
71
  };
71
72
  const ySpacer = {
72
73
  height: 8,
@@ -76,7 +77,7 @@ const flex1 = {
76
77
  };
77
78
  const fullscreen = {};
78
79
  const PlayPauseButton = ({ playing }) => playing ? (0, jsx_runtime_1.jsx)(icons_js_1.PauseIcon, {}) : (0, jsx_runtime_1.jsx)(icons_js_1.PlayIcon, {});
79
- const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, onSeekEnd, onSeekStart, inFrame, outFrame, initiallyShowControls, playerWidth, renderPlayPauseButton, renderFullscreenButton, alwaysShowControls, }) => {
80
+ const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVolumeControls, onFullscreenButtonClick, allowFullscreen, onExitFullscreenButtonClick, spaceKeyToPlayOrPause, onSeekEnd, onSeekStart, inFrame, outFrame, initiallyShowControls, playerWidth, renderPlayPauseButton, renderFullscreenButton, alwaysShowControls, showPlaybackRateControl, }) => {
80
81
  const playButtonRef = (0, react_1.useRef)(null);
81
82
  const frame = remotion_1.Internals.Timeline.useTimelinePosition();
82
83
  const [supportsFullscreen, setSupportsFullscreen] = (0, react_1.useState)(false);
@@ -146,7 +147,24 @@ const Controls = ({ durationInFrames, hovered, isFullscreen, fps, player, showVo
146
147
  textOverflow: 'ellipsis',
147
148
  };
148
149
  }, [maxTimeLabelWidth]);
149
- 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: renderPlayPauseButton === null ? ((0, jsx_runtime_1.jsx)(PlayPauseButton, { playing: player.playing })) : (renderPlayPauseButton({ playing: player.playing })) }), 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_js_1.MediaVolumeSlider, { displayVerticalVolumeSlider: displayVerticalVolumeSlider })] })) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_js_1.formatTime)(frame / fps), " / ", (0, format_time_js_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
150
+ const playbackRates = (0, react_1.useMemo)(() => {
151
+ if (showPlaybackRateControl === true) {
152
+ return [0.5, 0.8, 1, 1.2, 1.5, 1.8, 2, 2.5, 3];
153
+ }
154
+ if (Array.isArray(showPlaybackRateControl)) {
155
+ for (const rate of showPlaybackRateControl) {
156
+ if (typeof rate !== 'number') {
157
+ throw new Error('Every item in showPlaybackRateControl must be a number');
158
+ }
159
+ if (rate <= 0) {
160
+ throw new Error('Every item in showPlaybackRateControl must be positive');
161
+ }
162
+ }
163
+ return showPlaybackRateControl;
164
+ }
165
+ return null;
166
+ }, [showPlaybackRateControl]);
167
+ 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: renderPlayPauseButton === null ? ((0, jsx_runtime_1.jsx)(PlayPauseButton, { playing: player.playing })) : (renderPlayPauseButton({ playing: player.playing })) }), 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_js_1.MediaVolumeSlider, { displayVerticalVolumeSlider: displayVerticalVolumeSlider })] })) : null, (0, jsx_runtime_1.jsx)("div", { style: xSpacer }), (0, jsx_runtime_1.jsxs)("div", { style: timeLabel, children: [(0, format_time_js_1.formatTime)(frame / fps), " / ", (0, format_time_js_1.formatTime)(durationInFrames / fps)] }), (0, jsx_runtime_1.jsx)("div", { style: xSpacer })] }), (0, jsx_runtime_1.jsx)("div", { style: flex1 }), playbackRates && (0, jsx_runtime_1.jsx)(PlaybackrateControl_js_1.PlaybackrateControl, { playbackRates: playbackRates }), playbackRates && supportsFullscreen && allowFullscreen ? ((0, jsx_runtime_1.jsx)("div", { style: xSpacer })) : null, (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
150
168
  ? onExitFullscreenButtonClick
151
169
  : onFullscreenButtonClick, children: renderFullscreenButton === null ? ((0, jsx_runtime_1.jsx)(icons_js_1.FullscreenIcon, { isFullscreen: isFullscreen })) : (renderFullscreenButton({ isFullscreen })) })) : null })] }), (0, jsx_runtime_1.jsx)("div", { style: ySpacer }), (0, jsx_runtime_1.jsx)(PlayerSeekBar_js_1.PlayerSeekBar, { onSeekEnd: onSeekEnd, onSeekStart: onSeekStart, durationInFrames: durationInFrames, inFrame: inFrame, outFrame: outFrame })] }));
152
170
  };
@@ -35,5 +35,6 @@ declare const _default: React.ForwardRefExoticComponent<{
35
35
  renderPlayPauseButton: RenderPlayPauseButton | null;
36
36
  renderFullscreen: RenderFullscreenButton | null;
37
37
  alwaysShowControls: boolean;
38
+ showPlaybackRateControl: boolean | number[];
38
39
  } & React.RefAttributes<PlayerRef>>;
39
40
  export default _default;
@@ -41,7 +41,7 @@ if (reactVersion === '0') {
41
41
  throw new Error(`Version ${reactVersion} of "react" is not supported by Remotion`);
42
42
  }
43
43
  const doesReactVersionSupportSuspense = parseInt(reactVersion, 10) >= 18;
44
- const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, doubleClickToFullscreen, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, inFrame, outFrame, initiallyShowControls, renderFullscreen: renderFullscreenButton, renderPlayPauseButton, alwaysShowControls, }, ref) => {
44
+ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps, clickToPlay, showVolumeControls, doubleClickToFullscreen, spaceKeyToPlayOrPause, errorFallback, playbackRate, renderLoading, renderPoster, className, moveToBeginningWhenEnded, showPosterWhenUnplayed, showPosterWhenEnded, showPosterWhenPaused, inFrame, outFrame, initiallyShowControls, renderFullscreen: renderFullscreenButton, renderPlayPauseButton, alwaysShowControls, showPlaybackRateControl, }, ref) => {
45
45
  var _a, _b, _c, _d;
46
46
  const config = remotion_1.Internals.useUnsafeVideoConfig();
47
47
  const video = remotion_1.Internals.useVideo();
@@ -317,7 +317,7 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
317
317
  showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
318
318
  showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
319
319
  ].some(Boolean);
320
- 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_js_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_c = video === null || video === void 0 ? void 0 : video.defaultProps) !== null && _c !== void 0 ? _c : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) })) : 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_js_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, initiallyShowControls: initiallyShowControls, playerWidth: (_d = canvasSize === null || canvasSize === void 0 ? void 0 : canvasSize.width) !== null && _d !== void 0 ? _d : 0, renderFullscreenButton: renderFullscreenButton, renderPlayPauseButton: renderPlayPauseButton, alwaysShowControls: alwaysShowControls })) : null] }));
320
+ 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_js_1.PLAYER_CSS_CLASSNAME, children: VideoComponent ? ((0, jsx_runtime_1.jsx)(error_boundary_js_1.ErrorBoundary, { onError: onError, errorFallback: errorFallback, children: (0, jsx_runtime_1.jsx)(VideoComponent, { ...((_c = video === null || video === void 0 ? void 0 : video.defaultProps) !== null && _c !== void 0 ? _c : {}), ...(inputProps !== null && inputProps !== void 0 ? inputProps : {}) }) })) : 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_js_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, initiallyShowControls: initiallyShowControls, playerWidth: (_d = canvasSize === null || canvasSize === void 0 ? void 0 : canvasSize.width) !== null && _d !== void 0 ? _d : 0, renderFullscreenButton: renderFullscreenButton, renderPlayPauseButton: renderPlayPauseButton, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl })) : null] }));
321
321
  if (is_node_js_1.IS_NODE && !doesReactVersionSupportSuspense) {
322
322
  return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, className: className, children: content }));
323
323
  }
@@ -8,3 +8,6 @@ export declare const FullscreenIcon: React.FC<{
8
8
  }>;
9
9
  export declare const VolumeOffIcon: React.FC;
10
10
  export declare const VolumeOnIcon: React.FC;
11
+ export declare const SettingsIcon: React.FC<{
12
+ iconSize?: number;
13
+ }>;
package/dist/cjs/icons.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VolumeOnIcon = exports.VolumeOffIcon = exports.FullscreenIcon = exports.PauseIcon = exports.PlayIcon = exports.fullscreenIconSize = exports.ICON_SIZE = void 0;
3
+ exports.SettingsIcon = exports.VolumeOnIcon = exports.VolumeOffIcon = exports.FullscreenIcon = exports.PauseIcon = exports.PlayIcon = exports.fullscreenIconSize = exports.ICON_SIZE = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  exports.ICON_SIZE = 25;
6
6
  exports.fullscreenIconSize = 16;
@@ -48,3 +48,7 @@ const VolumeOnIcon = () => {
48
48
  return ((0, jsx_runtime_1.jsx)("svg", { width: exports.ICON_SIZE, height: exports.ICON_SIZE, viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { d: "M3 10v4c0 .55.45 1 1 1h3l3.29 3.29c.63.63 1.71.18 1.71-.71V6.41c0-.89-1.08-1.34-1.71-.71L7 9H4c-.55 0-1 .45-1 1zm13.5 2A4.5 4.5 0 0014 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 4.45v.2c0 .38.25.71.6.85C17.18 6.53 19 9.06 19 12s-1.82 5.47-4.4 6.5c-.36.14-.6.47-.6.85v.2c0 .63.63 1.07 1.21.85C18.6 19.11 21 15.84 21 12s-2.4-7.11-5.79-8.4c-.58-.23-1.21.22-1.21.85z", fill: "#fff" }) }));
49
49
  };
50
50
  exports.VolumeOnIcon = VolumeOnIcon;
51
+ const SettingsIcon = ({ iconSize }) => {
52
+ return ((0, jsx_runtime_1.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", height: iconSize !== null && iconSize !== void 0 ? iconSize : exports.ICON_SIZE, viewBox: "0 0 512 512", style: { fill: '#fff' }, children: (0, jsx_runtime_1.jsx)("path", { d: "M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z" }) }));
53
+ };
54
+ exports.SettingsIcon = SettingsIcon;
@@ -0,0 +1,6 @@
1
+ /// <reference types="react" />
2
+ export default function useComponentVisible(initialIsVisible: boolean): {
3
+ ref: import("react").RefObject<HTMLDivElement>;
4
+ isComponentVisible: boolean;
5
+ setIsComponentVisible: import("react").Dispatch<import("react").SetStateAction<boolean>>;
6
+ };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_1 = require("react");
4
+ // hook to hide a popup/modal when clicked outside
5
+ function useComponentVisible(initialIsVisible) {
6
+ const [isComponentVisible, setIsComponentVisible] = (0, react_1.useState)(initialIsVisible);
7
+ const ref = (0, react_1.useRef)(null);
8
+ (0, react_1.useEffect)(() => {
9
+ const handleClickOutside = (event) => {
10
+ if (ref.current && !ref.current.contains(event.target)) {
11
+ setIsComponentVisible(false);
12
+ }
13
+ };
14
+ document.addEventListener('pointerup', handleClickOutside, true);
15
+ return () => {
16
+ document.removeEventListener('pointerup', handleClickOutside, true);
17
+ };
18
+ }, []);
19
+ return { ref, isComponentVisible, setIsComponentVisible };
20
+ }
21
+ exports.default = useComponentVisible;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export declare const Checkmark: () => JSX.Element;
3
+ export declare const PlaybackrateControl: React.FC<{
4
+ playbackRates: number[];
5
+ }>;
@@ -42,6 +42,7 @@ export declare type PlayerProps<Schema extends AnyZodObject, Props> = {
42
42
  alwaysShowControls?: boolean;
43
43
  schema?: Schema;
44
44
  initiallyMuted?: boolean;
45
+ showPlaybackRateControl?: boolean | number[];
45
46
  } & CompProps<Props> & PropsIfHasProps<Schema, Props>;
46
47
  export declare const componentOrNullIfLazy: <Props>(props: CompProps<Props>) => ComponentType<Props> | null;
47
48
  /**
@@ -37,4 +37,5 @@ export declare const Controls: React.FC<{
37
37
  renderPlayPauseButton: RenderPlayPauseButton | null;
38
38
  renderFullscreenButton: RenderFullscreenButton | null;
39
39
  alwaysShowControls: boolean;
40
+ showPlaybackRateControl: boolean | number[];
40
41
  }>;
@@ -35,5 +35,6 @@ declare const _default: React.ForwardRefExoticComponent<{
35
35
  renderPlayPauseButton: RenderPlayPauseButton | null;
36
36
  renderFullscreen: RenderFullscreenButton | null;
37
37
  alwaysShowControls: boolean;
38
+ showPlaybackRateControl: boolean | number[];
38
39
  } & React.RefAttributes<PlayerRef>>;
39
40
  export default _default;
@@ -8,3 +8,6 @@ export declare const FullscreenIcon: React.FC<{
8
8
  }>;
9
9
  export declare const VolumeOffIcon: React.FC;
10
10
  export declare const VolumeOnIcon: React.FC;
11
+ export declare const SettingsIcon: React.FC<{
12
+ iconSize?: number;
13
+ }>;