@remotion/player 3.3.96 → 3.3.98

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;
@@ -40,6 +40,7 @@ export declare type PlayerProps<T> = {
40
40
  renderFullscreenButton?: RenderFullscreenButton;
41
41
  alwaysShowControls?: boolean;
42
42
  initiallyMuted?: boolean;
43
+ showPlaybackRateControl?: boolean | number[];
43
44
  } & PropsIfHasProps<T> & CompProps<T>;
44
45
  export declare const componentOrNullIfLazy: <T>(props: CompProps<T>) => ComponentType<T> | null;
45
46
  /**
@@ -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
  }
@@ -104,10 +105,13 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
104
105
  numberOfSharedAudioTags < 0) {
105
106
  throw new TypeError(`'numberOfSharedAudioTags' must be an integer but got '${numberOfSharedAudioTags}' instead`);
106
107
  }
107
- (0, validate_playbackrate_js_1.validatePlaybackRate)(playbackRate);
108
+ (0, validate_playbackrate_js_1.validatePlaybackRate)(currentPlaybackRate);
108
109
  (0, react_1.useEffect)(() => {
109
- emitter.dispatchRateChange(playbackRate);
110
- }, [emitter, playbackRate]);
110
+ emitter.dispatchRateChange(currentPlaybackRate);
111
+ }, [emitter, currentPlaybackRate]);
112
+ (0, react_1.useEffect)(() => {
113
+ setCurrentPlaybackRate(playbackRate);
114
+ }, [playbackRate]);
111
115
  (0, react_1.useImperativeHandle)(ref, () => rootRef.current, []);
112
116
  const timelineContextValue = (0, react_1.useMemo)(() => {
113
117
  return {
@@ -115,14 +119,14 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
115
119
  playing,
116
120
  rootId,
117
121
  shouldRegisterSequences: false,
118
- playbackRate,
122
+ playbackRate: currentPlaybackRate,
119
123
  imperativePlaying,
120
- setPlaybackRate: () => {
121
- throw new Error('playback rate');
124
+ setPlaybackRate: (rate) => {
125
+ setCurrentPlaybackRate(rate);
122
126
  },
123
127
  audioAndVideoTags,
124
128
  };
125
- }, [frame, playbackRate, playing, rootId]);
129
+ }, [frame, currentPlaybackRate, playing, rootId]);
126
130
  const setTimelineContextValue = (0, react_1.useMemo)(() => {
127
131
  return {
128
132
  setFrame,
@@ -141,7 +145,7 @@ const PlayerFn = ({ durationInFrames, compositionHeight, compositionWidth, fps,
141
145
  }
142
146
  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: inputProps, 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'
143
147
  ? clickToPlay
144
- : 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 }) }) }) }) }));
148
+ : 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 }) }) }) }) }));
145
149
  };
146
150
  const forward = react_1.forwardRef;
147
151
  /**
@@ -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, _e;
46
46
  const config = remotion_1.Internals.useUnsafeVideoConfig();
47
47
  const video = remotion_1.Internals.useVideo();
@@ -168,6 +168,15 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
168
168
  }, [player.emitter, scale]);
169
169
  const { setMediaVolume, setMediaMuted } = (0, react_1.useContext)(remotion_1.Internals.SetMediaVolumeContext);
170
170
  const { mediaMuted, mediaVolume } = (0, react_1.useContext)(remotion_1.Internals.MediaVolumeContext);
171
+ (0, react_1.useEffect)(() => {
172
+ player.emitter.dispatchVolumeChange(mediaVolume);
173
+ }, [player.emitter, mediaVolume]);
174
+ const isMuted = mediaMuted || mediaVolume === 0;
175
+ (0, react_1.useEffect)(() => {
176
+ player.emitter.dispatchMuteChange({
177
+ isMuted,
178
+ });
179
+ }, [player.emitter, isMuted]);
171
180
  (0, react_1.useImperativeHandle)(ref, () => {
172
181
  const methods = {
173
182
  play: player.play,
@@ -209,9 +218,8 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
209
218
  throw new TypeError(`setVolume() got a number that is out of range. Must be between 0 and 1, got ${typeof vol}`);
210
219
  }
211
220
  setMediaVolume(vol);
212
- player.emitter.dispatchVolumeChange(vol);
213
221
  },
214
- isMuted: () => mediaMuted || mediaVolume === 0,
222
+ isMuted: () => isMuted,
215
223
  mute: () => {
216
224
  setMediaMuted(true);
217
225
  },
@@ -227,6 +235,7 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
227
235
  isFullscreen,
228
236
  loop,
229
237
  mediaMuted,
238
+ isMuted,
230
239
  mediaVolume,
231
240
  player,
232
241
  requestFullscreen,
@@ -308,7 +317,7 @@ const PlayerUI = ({ controls, style, loop, autoPlay, allowFullscreen, inputProps
308
317
  showPosterWhenEnded && player.isLastFrame && !player.isPlaying(),
309
318
  showPosterWhenUnplayed && !player.hasPlayed && !player.isPlaying(),
310
319
  ].some(Boolean);
311
- 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 : {}), ...((_d = inputProps) !== null && _d !== void 0 ? _d : {}) }) })) : 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: (_e = canvasSize === null || canvasSize === void 0 ? void 0 : canvasSize.width) !== null && _e !== void 0 ? _e : 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 : {}), ...((_d = inputProps) !== null && _d !== void 0 ? _d : {}) }) })) : 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: (_e = canvasSize === null || canvasSize === void 0 ? void 0 : canvasSize.width) !== null && _e !== void 0 ? _e : 0, renderFullscreenButton: renderFullscreenButton, renderPlayPauseButton: renderPlayPauseButton, alwaysShowControls: alwaysShowControls, showPlaybackRateControl: showPlaybackRateControl })) : null] }));
312
321
  if (is_node_js_1.IS_NODE && !doesReactVersionSupportSuspense) {
313
322
  return ((0, jsx_runtime_1.jsx)("div", { ref: container, style: outerStyle, className: className, children: content }));
314
323
  }
@@ -22,6 +22,9 @@ declare type VolumeChangeEventPayload = {
22
22
  declare type FullscreenChangeEventPayload = {
23
23
  isFullscreen: boolean;
24
24
  };
25
+ declare type MuteChangeEventPayload = {
26
+ isMuted: boolean;
27
+ };
25
28
  declare type PlayerStateEventMap = {
26
29
  seeked: SeekPayload;
27
30
  pause: undefined;
@@ -34,6 +37,7 @@ declare type PlayerStateEventMap = {
34
37
  timeupdate: TimeUpdateEventPayload;
35
38
  frameupdate: FrameUpdateEventPayload;
36
39
  fullscreenchange: FullscreenChangeEventPayload;
40
+ mutechange: MuteChangeEventPayload;
37
41
  };
38
42
  declare type ThumbnailStateEventMap = {
39
43
  error: ErrorPayload;
@@ -65,6 +69,7 @@ export declare class PlayerEmitter {
65
69
  dispatchTimeUpdate(event: TimeUpdateEventPayload): void;
66
70
  dispatchFrameUpdate(event: FrameUpdateEventPayload): void;
67
71
  dispatchFullscreenChange(event: FullscreenChangeEventPayload): void;
72
+ dispatchMuteChange(event: MuteChangeEventPayload): void;
68
73
  }
69
74
  export declare class ThumbnailEmitter {
70
75
  listeners: ThumbnailListeners;
@@ -15,6 +15,7 @@ class PlayerEmitter {
15
15
  frameupdate: [],
16
16
  fullscreenchange: [],
17
17
  volumechange: [],
18
+ mutechange: [],
18
19
  };
19
20
  }
20
21
  addEventListener(name, callback) {
@@ -71,6 +72,9 @@ class PlayerEmitter {
71
72
  dispatchFullscreenChange(event) {
72
73
  this.dispatchEvent('fullscreenchange', event);
73
74
  }
75
+ dispatchMuteChange(event) {
76
+ this.dispatchEvent('mutechange', event);
77
+ }
74
78
  }
75
79
  exports.PlayerEmitter = PlayerEmitter;
76
80
  class ThumbnailEmitter {
@@ -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
+ }>;
@@ -40,6 +40,7 @@ export declare type PlayerProps<T> = {
40
40
  renderFullscreenButton?: RenderFullscreenButton;
41
41
  alwaysShowControls?: boolean;
42
42
  initiallyMuted?: boolean;
43
+ showPlaybackRateControl?: boolean | number[];
43
44
  } & PropsIfHasProps<T> & CompProps<T>;
44
45
  export declare const componentOrNullIfLazy: <T>(props: CompProps<T>) => ComponentType<T> | null;
45
46
  /**
@@ -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;
@@ -22,6 +22,9 @@ declare type VolumeChangeEventPayload = {
22
22
  declare type FullscreenChangeEventPayload = {
23
23
  isFullscreen: boolean;
24
24
  };
25
+ declare type MuteChangeEventPayload = {
26
+ isMuted: boolean;
27
+ };
25
28
  declare type PlayerStateEventMap = {
26
29
  seeked: SeekPayload;
27
30
  pause: undefined;
@@ -34,6 +37,7 @@ declare type PlayerStateEventMap = {
34
37
  timeupdate: TimeUpdateEventPayload;
35
38
  frameupdate: FrameUpdateEventPayload;
36
39
  fullscreenchange: FullscreenChangeEventPayload;
40
+ mutechange: MuteChangeEventPayload;
37
41
  };
38
42
  declare type ThumbnailStateEventMap = {
39
43
  error: ErrorPayload;
@@ -65,6 +69,7 @@ export declare class PlayerEmitter {
65
69
  dispatchTimeUpdate(event: TimeUpdateEventPayload): void;
66
70
  dispatchFrameUpdate(event: FrameUpdateEventPayload): void;
67
71
  dispatchFullscreenChange(event: FullscreenChangeEventPayload): void;
72
+ dispatchMuteChange(event: MuteChangeEventPayload): void;
68
73
  }
69
74
  export declare class ThumbnailEmitter {
70
75
  listeners: ThumbnailListeners;
@@ -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
+ }>;