112 | );
113 | }
114 | }
115 |
116 | // eslint-disable-next-line react/no-deprecated
117 | ReactDOM.render(, document.getElementById('example'));
118 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @u-wave/react-vimeo
2 |
3 | Vimeo player component for React.
4 |
5 | [Install][] - [Usage][] - [Demo][] - [Props][]
6 |
7 | ## Install
8 |
9 | ```
10 | npm install --save @u-wave/react-vimeo
11 | ```
12 |
13 | ## Usage
14 |
15 | [Demo][] - [Demo source code][]
16 |
17 | ```js
18 | import Vimeo from '@u-wave/react-vimeo';
19 |
20 |
24 | ```
25 |
26 | ## Props
27 | | Name | Type | Default | Description |
28 | |:-----|:-----|:-----|:-----|
29 | | video | number, string | | A Vimeo video ID or URL. |
30 | | id | string | | DOM ID for the player element. |
31 | | className | string | | CSS className for the player element. |
32 | | style | object | | Inline style for container element. |
33 | | width | number, string | | Width of the player element. |
34 | | height | number, string | | Height of the player element. |
35 | | paused | bool | | Pause the video. |
36 | | volume | number | | The playback volume as a number between 0 and 1. |
37 | | start | number | | The time in seconds at which to start playing the video. |
38 | | autopause | bool | true | Pause this video automatically when another one plays. |
39 | | autoplay | bool | false | Automatically start playback of the video. Note that this won’t work on some devices. |
40 | | showByline | bool | true | Show the byline on the video. |
41 | | color | string | | Specify the color of the video controls. Colors may be overridden by the embed settings of the video. _(Ex: "ef2f9f")_ |
42 | | dnt | bool | false | Blocks the player from tracking any session data, including all cookies and analytics. |
43 | | controls | bool | true | Hide all elements in the player, such as the progress bar, sharing buttons, etc. (requires Vimeo PRO / Business account) |
44 | | loop | bool | false | Play the video again when it reaches the end. |
45 | | showPortrait | bool | true | Show the portrait on the video. |
46 | | showTitle | bool | true | Show the title on the video. |
47 | | muted | bool | false | Starts in a muted state to help with autoplay |
48 | | background | bool | false | Starts in a background state with no controls to help with autoplay |
49 | | responsive | bool | false | Enable responsive mode and resize according to parent element (experimental) |
50 | | playbackRate | number | | Specify playback rate (requires Vimeo PRO / Business account)
51 | | speed | bool | false | Enable playback rate controls (requires Vimeo PRO / Business account) |
52 | | keyboard | bool | true | Allows for keyboard input to trigger player events. |
53 | | pip | bool | false | Show the picture-in-picture button in the controlbar and enable the picture-in-picture API. |
54 | | playsInline | bool | true | Play video inline on mobile devices, to automatically go fullscreen on playback set this parameter to false. |
55 | | quality | string | | Vimeo Plus, PRO, and Business members can default an embedded video to a specific quality on desktop. |
56 | | textTrack | string | | Turn captions/subtitles on for a specific language by default. |
57 | | transparent | bool | true | The responsive player and transparent background are enabled by default, to disable set this parameter to false. |
58 | | onReady | function | | Sent when the Vimeo player API has loaded. Receives the Vimeo player object in the first parameter. |
59 | | onError | function | | Sent when the player triggers an error. |
60 | | onPlay | function | | Triggered when video playback is initiated. |
61 | | onPlaying | function | | Triggered when the video starts playing. |
62 | | onPause | function | | Triggered when the video pauses. |
63 | | onEnd | function | | Triggered any time the video playback reaches the end. Note: when `loop` is turned on, the ended event will not fire. |
64 | | onTimeUpdate | function | | Triggered as the `currentTime` of the video updates. It generally fires every 250ms, but it may vary depending on the browser. |
65 | | onProgress | function | | Triggered as the video is loaded. Reports back the amount of the video that has been buffered. |
66 | | onSeeked | function | | Triggered when the player seeks to a specific time. An `onTimeUpdate` event will also be fired at the same time. |
67 | | onTextTrackChange | function | | Triggered when the active text track (captions/subtitles) changes. The values will be `null` if text tracks are turned off. |
68 | | onCueChange | function | | Triggered when the active cue for the current text track changes. It also fires when the active text track changes. There may be multiple cues active. |
69 | | onCuePoint | function | | Triggered when the current time hits a registered cue point. |
70 | | onVolumeChange | function | | Triggered when the volume in the player changes. Some devices do not support setting the volume of the video independently from the system volume, so this event will never fire on those devices. |
71 | | onPlaybackRateChange | function | | Triggered when the playback rate changes. |
72 | | onLoaded | function | | Triggered when a new video is loaded in the player. |
73 |
74 | ## Related
75 |
76 | - [@u-wave/react-youtube][] - A YouTube component with a similar declarative API.
77 | - [react-dailymotion][] - A Dailymotion component with a similar declarative API.
78 |
79 | ## License
80 |
81 | [MIT]
82 |
83 | [Install]: #install
84 | [Usage]: #usage
85 | [Props]: #props
86 | [Demo]: https://u-wave.github.io/react-vimeo
87 | [Demo source code]: ./example
88 | [MIT]: ./LICENSE
89 | [@u-wave/react-youtube]: https://github.com/u-wave/react-youtube
90 | [react-dailymotion]: https://github.com/u-wave/react-dailymotion
91 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | import expect, { createSpy } from 'expect';
2 | import render from './util/render';
3 |
4 | describe('Vimeo', () => {
5 | it('should create a Vimeo player when mounted', async () => {
6 | const onReady = createSpy();
7 | const { sdkMock, playerMock } = await render({
8 | video: 169408731,
9 | onReady,
10 | });
11 | expect(sdkMock).toHaveBeenCalled();
12 | expect(sdkMock.calls[0].arguments[1]).toMatch({ id: 169408731 });
13 | await playerMock.ready();
14 | expect(onReady).toHaveBeenCalled();
15 | expect(onReady.calls[0].arguments[0]).toBe(playerMock);
16 | });
17 |
18 | it('should use `url` prop for full vimeo URLs', async () => {
19 | const { sdkMock } = await render({ video: 'https://vimeo.com/179290396' });
20 | expect(sdkMock).toHaveBeenCalled();
21 | expect(sdkMock.calls[0].arguments[1]).toMatch({ url: 'https://vimeo.com/179290396' });
22 | });
23 |
24 | it('should all onError when `ready()` fails', async () => {
25 | const onError = createSpy();
26 | const { sdkMock } = await render({
27 | video: 404,
28 | shouldFail: true,
29 | onError,
30 | });
31 | await Promise.resolve();
32 | expect(sdkMock).toHaveBeenCalled();
33 | expect(sdkMock.calls[0].arguments[1]).toMatch({ id: 404 });
34 | expect(onError).toHaveBeenCalled();
35 | expect(onError.calls[0].arguments[0]).toEqual(new Error('artificial failure'));
36 | });
37 |
38 | it('should load a different video when "video" prop changes', async () => {
39 | const { sdkMock, playerMock, rerender } = await render({
40 | video: 169408731,
41 | });
42 | expect(sdkMock).toHaveBeenCalled();
43 | expect(sdkMock.calls[0].arguments[1]).toMatch({ id: 169408731 });
44 |
45 | await rerender({ video: 162959050 });
46 |
47 | expect(playerMock.loadVideo).toHaveBeenCalled();
48 | expect(playerMock.loadVideo.calls[0].arguments[0]).toEqual(162959050);
49 | });
50 |
51 | it('should pause the video using the "paused" prop', async () => {
52 | const { playerMock, rerender } = await render({
53 | video: 169408731,
54 | autoplay: true,
55 | });
56 |
57 | // Don't call `play` again when we were already playing
58 | await rerender({ paused: false });
59 | expect(playerMock.play).toNotHaveBeenCalled();
60 |
61 | await rerender({ paused: true });
62 | expect(playerMock.pause).toHaveBeenCalled();
63 |
64 | await rerender({ paused: false });
65 | expect(playerMock.play).toHaveBeenCalled();
66 | });
67 |
68 | it('should set the volume using the "volume" prop', async () => {
69 | const { playerMock, rerender } = await render({
70 | video: 169408731,
71 | volume: 0.5,
72 | });
73 | expect(playerMock.setVolume).toHaveBeenCalledWith(0.5);
74 |
75 | await rerender({ volume: 1 });
76 |
77 | expect(playerMock.setVolume).toHaveBeenCalledWith(1);
78 | });
79 |
80 | it('should set the start time using the "start" prop', async () => {
81 | const { playerMock, rerender } = await render({
82 | video: 169408731,
83 | start: 60,
84 | });
85 | expect(playerMock.setCurrentTime).toHaveBeenCalledWith(60);
86 |
87 | playerMock.setCurrentTime.reset();
88 | await rerender({ start: 90 });
89 | expect(playerMock.setCurrentTime).toNotHaveBeenCalled();
90 |
91 | await rerender({ video: 169408732, start: 120 });
92 | expect(playerMock.setCurrentTime).toHaveBeenCalledWith(120);
93 | });
94 |
95 | it('should set the player color using the "color" prop', async () => {
96 | const { playerMock, sdkMock, rerender } = await render({
97 | video: 169408731,
98 | color: '#0000ff',
99 | });
100 | expect(sdkMock).toHaveBeenCalled();
101 | expect(sdkMock.calls[0].arguments[1]).toMatch({ color: '#0000ff' });
102 |
103 | await rerender({ color: '#ff0000' });
104 | expect(playerMock.setColor).toHaveBeenCalledWith('#ff0000');
105 | await rerender({ color: '#00ff00' });
106 | expect(playerMock.setColor).toHaveBeenCalledWith('#00ff00');
107 | });
108 |
109 | it('should set the looping flag using the "loop" prop', async () => {
110 | const { playerMock, sdkMock, rerender } = await render({
111 | video: 169408731,
112 | loop: false,
113 | });
114 | expect(sdkMock).toHaveBeenCalled();
115 | expect(sdkMock.calls[0].arguments[1]).toMatch({ loop: false });
116 |
117 | await rerender({ loop: true });
118 | expect(playerMock.setLoop).toHaveBeenCalledWith(true);
119 | await rerender({ loop: false });
120 | expect(playerMock.setLoop).toHaveBeenCalledWith(false);
121 | });
122 |
123 | it('should set the iframe width/height using the width/height props', async () => {
124 | const { sdkMock, playerMock, rerender } = await render({
125 | video: 169408731,
126 | width: 640,
127 | height: 320,
128 | });
129 | expect(sdkMock.calls[0].arguments[1]).toMatch({
130 | width: 640,
131 | height: 320,
132 | });
133 |
134 | await rerender({
135 | width: '100%',
136 | height: 800,
137 | });
138 |
139 | expect(playerMock.setWidth).toHaveBeenCalledWith('100%');
140 | expect(playerMock.setHeight).toHaveBeenCalledWith(800);
141 | });
142 |
143 | it('should set the playback rate using the "playbackRate" props', async () => {
144 | const { playerMock, rerender } = await render({
145 | video: 169408731,
146 | playbackRate: 0.5,
147 | });
148 |
149 | expect(playerMock.setPlaybackRate).toHaveBeenCalledWith(0.5);
150 |
151 | await rerender({ playbackRate: 2 });
152 |
153 | expect(playerMock.setPlaybackRate).toHaveBeenCalledWith(2);
154 | });
155 |
156 | it('should destroy player when unmounting', async () => {
157 | const { playerMock, unmount } = await render({
158 | video: 169408731,
159 | width: 640,
160 | height: 320,
161 | });
162 |
163 | unmount();
164 |
165 | expect(playerMock.destroy).toHaveBeenCalled();
166 | });
167 | });
168 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import Player, { Error } from '@vimeo/player'
3 |
4 | export type PlayEvent = {
5 | /**
6 | * The length of the video in seconds.
7 | */
8 | duration: number
9 | /**
10 | * The amount of the video, in seconds, that has played.
11 | */
12 | seconds: number
13 | /**
14 | * The amount of the video that has played in comparison to the length of the video;
15 | * multiply by 100 to obtain the percentage.
16 | */
17 | percent: number
18 | }
19 |
20 | export type PlayingEvent = PlayEvent;
21 |
22 | export type PauseEvent = {
23 | /**
24 | * The length of the video in seconds.
25 | */
26 | duration: number
27 | /**
28 | * The amount of the video, in seconds, that has played to the pause position.
29 | */
30 | seconds: number
31 | /**
32 | * The amount of the video that has played to the pause position in comparison to the length of the video; multiply by 100 to obtain the percentage.
33 | */
34 | percent: number
35 | }
36 |
37 | export type EndEvent = PauseEvent
38 |
39 | export type TimeUpdateEvent = {
40 | /**
41 | * The length of the video in seconds.
42 | */
43 | duration: number
44 | /**
45 | * The amount of the video, in seconds, that has played from the current playback position.
46 | */
47 | seconds: number
48 | /**
49 | * The amount of the video that has played from the current playback position in comparison to the length of the video; multiply by 100 to obtain the percentage.
50 | */
51 | percent: number
52 | }
53 |
54 | export type ProgressEvent = {
55 | /**
56 | * The length of the video in seconds.
57 | */
58 | duration: number
59 | /**
60 | * The amount of the video, in seconds, that has buffered.
61 | */
62 | seconds: number
63 | /**
64 | * The amount of the video that has buffered in comparison to the length of the video;
65 | * multiply by 100 to obtain the percentage.
66 | */
67 | percent: number
68 | }
69 |
70 | export type SeekedEvent = {
71 | /**
72 | * The length of the video in seconds.
73 | */
74 | duration: number
75 | /**
76 | * The amount of the video, in seconds, that has played from the new seek position.
77 | */
78 | seconds: number
79 | /**
80 | * The amount of the video that has played from the new seek position in comparison to the length of the video; multiply by 100 to obtain the percentage.
81 | */
82 | percent: number
83 | }
84 |
85 | export type TextTrackEvent = {
86 | kind: 'captions' | 'subtitles'
87 | label: string
88 | language: string
89 | }
90 |
91 | export type Cue = {
92 | html: string
93 | text: string
94 | }
95 |
96 | export type CueChangeEvent = {
97 | cues: Cue[]
98 | kind: 'captions' | 'subtitles'
99 | label: string
100 | language: string
101 | }
102 |
103 | export type CuePointEvent = {
104 | /**
105 | * The location of the cue point in seconds.
106 | */
107 | time: number
108 | /**
109 | * The ID of the cue point.
110 | */
111 | id: string
112 | /**
113 | * The custom data from the `addCuePoint()` call, or an empty object.
114 | */
115 | data: object
116 | }
117 |
118 | export type VolumeEvent = {
119 | /**
120 | * The new volume level.
121 | */
122 | volume: number
123 | }
124 |
125 | export type PlaybackRateEvent = {
126 | /**
127 | * The new playback rate.
128 | */
129 | playbackRate: number
130 | }
131 |
132 | export type LoadEvent = {
133 | /**
134 | * The ID of the new video.
135 | */
136 | id: number
137 | }
138 |
139 | export interface VimeoProps {
140 | /**
141 | * A Vimeo video ID or URL.
142 | */
143 | video: number | string
144 | /**
145 | * DOM ID for the player element.
146 | */
147 | id?: string
148 | /**
149 | * CSS className for the player element.
150 | */
151 | className?: string
152 | /**
153 | * Inline style for container element.
154 | */
155 | style?: React.CSSProperties
156 | /**
157 | * Width of the player element.
158 | */
159 | width?: number | string
160 | /**
161 | * Height of the player element.
162 | */
163 | height?: number | string
164 |
165 | /**
166 | * Pause the video.
167 | */
168 | paused?: boolean
169 |
170 | /**
171 | * The playback volume as a number between 0 and 1.
172 | */
173 | volume?: number
174 |
175 | /**
176 | * The time in seconds at which to start playing the video.
177 | */
178 | start?: number
179 |
180 | /**
181 | * Pause this video automatically when another one plays.
182 | */
183 | autopause?: boolean
184 |
185 | /**
186 | * Automatically start playback of the video. Note that this won’t work on
187 | * some devices.
188 | */
189 | autoplay?: boolean
190 |
191 | /**
192 | * Show the byline on the video.
193 | */
194 | showByline?: boolean
195 |
196 | /**
197 | * Specify the color of the video controls. Colors may be overridden by the
198 | * embed settings of the video. _(Ex: "ef2f9f")_
199 | */
200 | color?: string
201 |
202 | /**
203 | * Hide all elements in the player, such as the progress bar, sharing buttons, etc.
204 | * (requires Vimeo PRO / Business account)
205 | */
206 | controls?: boolean
207 |
208 | /**
209 | * Play the video again when it reaches the end.
210 | */
211 | loop?: boolean
212 |
213 | /**
214 | * Show the portrait on the video.
215 | */
216 | showPortrait?: boolean
217 |
218 | /**
219 | * Show the title on the video.
220 | */
221 | showTitle?: boolean
222 |
223 | /**
224 | * Starts in a muted state to help with autoplay
225 | */
226 | muted?: boolean
227 |
228 | /**
229 | * Starts in a background state with no controls to help with autoplay
230 | */
231 | background?: boolean
232 |
233 | /**
234 | * Enable responsive mode and resize according to parent element (experimental)
235 | */
236 | responsive?: boolean
237 |
238 | /**
239 | * Specify playback rate (requires Vimeo PRO / Business account)
240 | */
241 | playbackRate?: number
242 |
243 | /**
244 | * Enable playback rate controls (requires Vimeo PRO / Business account)
245 | */
246 | speed?: boolean
247 |
248 | /**
249 | * Blocks the player from tracking any session data, including all cookies and analytics
250 | */
251 | dnt?: boolean
252 |
253 | /**
254 | * Allows for keyboard input to trigger player events.
255 | */
256 | keyboard?: boolean
257 |
258 | /**
259 | * Show the picture-in-picture button in the controlbar
260 | * and enable the picture-in-picture API.
261 | */
262 | pip?: boolean
263 |
264 | /**
265 | * Play video inline on mobile devices, to automatically
266 | * go fullscreen on playback set this parameter to false.
267 | */
268 | playsInline?: boolean
269 |
270 | /**
271 | * Vimeo Plus, PRO, and Business members can default
272 | * an embedded video to a specific quality on desktop.
273 | */
274 | quality?: string
275 |
276 | /**
277 | * Turn captions/subtitles on for a specific language by default.
278 | */
279 | textTrack?: string
280 |
281 | /**
282 | * The responsive player and transparent background are enabled
283 | * by default, to disable set this parameter to false.
284 | */
285 | transparent?: boolean
286 |
287 | /**
288 | * Sent when the Vimeo player API has loaded.
289 | * Receives the Vimeo player object in the first parameter.
290 | */
291 | onReady?: (player: Player) => void
292 | /**
293 | * Sent when the player triggers an error.
294 | */
295 | onError?: (error: Error) => void
296 | /**
297 | * Triggered when video playback is initiated.
298 | */
299 | onPlay?: (event: PlayEvent) => void
300 | /**
301 | * Triggered when the video starts playing.
302 | */
303 | onPlaying?: (event: PlayingEvent) => void
304 | /**
305 | * Triggered when the video pauses.
306 | */
307 | onPause?: (event: PauseEvent) => void
308 | /**
309 | * Triggered any time the video playback reaches the end.
310 | * Note: when `loop` is turned on, the ended event will not fire.
311 | */
312 | onEnd?: (event: EndEvent) => void
313 | /**
314 | * Triggered as the `currentTime` of the video updates. It generally fires
315 | * every 250ms, but it may vary depending on the browser.
316 | */
317 | onTimeUpdate?: (event: TimeUpdateEvent) => void
318 | /**
319 | * Triggered as the video is loaded. Reports back the amount of the video
320 | * that has been buffered.
321 | */
322 | onProgress?: (event: ProgressEvent) => void
323 | /**
324 | * Triggered when the player seeks to a specific time. An `onTimeUpdate`
325 | * event will also be fired at the same time.
326 | */
327 | onSeeked?: (event: SeekedEvent) => void
328 | /**
329 | * Triggered when the active text track (captions/subtitles) changes. The
330 | * values will be `null` if text tracks are turned off.
331 | */
332 | onTextTrackChange?: (event: TextTrackEvent) => void
333 | /**
334 | * Triggered when the active cue for the current text track changes. It also
335 | * fires when the active text track changes. There may be multiple cues
336 | * active.
337 | */
338 | onCueChange?: (event: CueChangeEvent) => void
339 | /**
340 | * Triggered when the current time hits a registered cue point.
341 | */
342 | onCuePoint?: (event: CuePointEvent) => void
343 | /**
344 | * Triggered when the volume in the player changes. Some devices do not
345 | * support setting the volume of the video independently from the system
346 | * volume, so this event will never fire on those devices.
347 | */
348 | onVolumeChange?: (event: VolumeEvent) => void
349 | /**
350 | * Triggered when the playback rate in the player changes.
351 | */
352 | onPlaybackRateChange?: (event: PlaybackRateEvent) => void
353 | /**
354 | * Triggered when a new video is loaded in the player.
355 | */
356 | onLoaded?: (event: LoadEvent) => void
357 | }
358 |
359 | /**
360 | * Vimeo player component for React.
361 | */
362 | export default class Vimeo extends React.Component {}
363 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Player from '@vimeo/player';
4 | import eventNames from './eventNames';
5 |
6 | class Vimeo extends React.Component {
7 | constructor(props) {
8 | super(props);
9 |
10 | this.refContainer = this.refContainer.bind(this);
11 | }
12 |
13 | componentDidMount() {
14 | this.createPlayer();
15 | }
16 |
17 | componentDidUpdate(prevProps) {
18 | // eslint-disable-next-line react/destructuring-assignment
19 | const changes = Object.keys(this.props).filter((name) => this.props[name] !== prevProps[name]);
20 |
21 | this.updateProps(changes);
22 | }
23 |
24 | componentWillUnmount() {
25 | this.player.destroy();
26 | }
27 |
28 | /**
29 | * @private
30 | */
31 | getInitialOptions() {
32 | const { video } = this.props;
33 | const videoType = /^https?:/i.test(video) ? 'url' : 'id';
34 | /* eslint-disable react/destructuring-assignment */
35 | return {
36 | [videoType]: video,
37 | width: this.props.width,
38 | height: this.props.height,
39 | autopause: this.props.autopause,
40 | autoplay: this.props.autoplay,
41 | byline: this.props.showByline,
42 | color: this.props.color,
43 | controls: this.props.controls,
44 | loop: this.props.loop,
45 | portrait: this.props.showPortrait,
46 | title: this.props.showTitle,
47 | muted: this.props.muted,
48 | background: this.props.background,
49 | responsive: this.props.responsive,
50 | dnt: this.props.dnt,
51 | speed: this.props.speed,
52 | keyboard: this.props.keyboard,
53 | pip: this.props.pip,
54 | playsinline: this.props.playsInline,
55 | quality: this.props.quality,
56 | texttrack: this.props.textTrack,
57 | transparent: this.props.transparent,
58 | };
59 | /* eslint-enable react/destructuring-assignment */
60 | }
61 |
62 | /**
63 | * @private
64 | */
65 | updateProps(propNames) {
66 | const { player } = this;
67 | propNames.forEach((name) => {
68 | // eslint-disable-next-line react/destructuring-assignment
69 | const value = this.props[name];
70 | switch (name) {
71 | case 'autopause':
72 | player.setAutopause(value);
73 | break;
74 | case 'color':
75 | player.setColor(value);
76 | break;
77 | case 'loop':
78 | player.setLoop(value);
79 | break;
80 | case 'volume':
81 | player.setVolume(value);
82 | break;
83 | case 'paused':
84 | player.getPaused().then((paused) => {
85 | if (value && !paused) {
86 | return player.pause();
87 | }
88 | if (!value && paused) {
89 | return player.play();
90 | }
91 | return null;
92 | });
93 | break;
94 | case 'width':
95 | case 'height':
96 | player.element[name] = value;
97 | break;
98 | case 'video':
99 | if (value) {
100 | const { start } = this.props;
101 | const loaded = player.loadVideo(value);
102 | // Set the start time only when loading a new video.
103 | // It seems like this has to be done after the video has loaded, else it just starts at
104 | // the beginning!
105 | if (typeof start === 'number') {
106 | loaded.then(() => {
107 | player.setCurrentTime(start);
108 | });
109 | }
110 | } else {
111 | player.unload();
112 | }
113 | break;
114 | case 'playbackRate':
115 | player.setPlaybackRate(value);
116 | break;
117 | case 'quality':
118 | player.setQuality(value);
119 | break;
120 | default:
121 | // Nothing
122 | }
123 | });
124 | }
125 |
126 | /**
127 | * @private
128 | */
129 | createPlayer() {
130 | const { start, volume, playbackRate } = this.props;
131 |
132 | this.player = new Player(this.container, this.getInitialOptions());
133 |
134 | Object.keys(eventNames).forEach((dmName) => {
135 | const reactName = eventNames[dmName];
136 | this.player.on(dmName, (event) => {
137 | // eslint-disable-next-line react/destructuring-assignment
138 | const handler = this.props[reactName];
139 | if (handler) {
140 | handler(event);
141 | }
142 | });
143 | });
144 |
145 | const { onError, onReady } = this.props;
146 | this.player.ready().then(() => {
147 | if (onReady) {
148 | onReady(this.player);
149 | }
150 | }, (err) => {
151 | if (onError) {
152 | onError(err);
153 | } else {
154 | throw err;
155 | }
156 | });
157 |
158 | if (typeof start === 'number') {
159 | this.player.setCurrentTime(start);
160 | }
161 |
162 | if (typeof volume === 'number') {
163 | this.updateProps(['volume']);
164 | }
165 |
166 | if (typeof playbackRate === 'number') {
167 | this.updateProps(['playbackRate']);
168 | }
169 | }
170 |
171 | /**
172 | * @private
173 | */
174 | refContainer(container) {
175 | this.container = container;
176 | }
177 |
178 | render() {
179 | const { id, className, style } = this.props;
180 |
181 | return (
182 |
188 | );
189 | }
190 | }
191 |
192 | if (process.env.NODE_ENV !== 'production') {
193 | Vimeo.propTypes = {
194 | /**
195 | * A Vimeo video ID or URL.
196 | */
197 | video: PropTypes.oneOfType([
198 | PropTypes.number,
199 | PropTypes.string,
200 | ]),
201 | /**
202 | * DOM ID for the player element.
203 | */
204 | id: PropTypes.string,
205 | /**
206 | * CSS className for the player element.
207 | */
208 | className: PropTypes.string,
209 | /**
210 | * Inline style for container element.
211 | */
212 | style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
213 | /**
214 | * Width of the player element.
215 | */
216 | width: PropTypes.oneOfType([
217 | PropTypes.number,
218 | PropTypes.string,
219 | ]),
220 | /**
221 | * Height of the player element.
222 | */
223 | height: PropTypes.oneOfType([
224 | PropTypes.number,
225 | PropTypes.string,
226 | ]),
227 |
228 | /**
229 | * Pause the video.
230 | */
231 | paused: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types
232 |
233 | /**
234 | * The playback volume as a number between 0 and 1.
235 | */
236 | volume: PropTypes.number,
237 |
238 | /**
239 | * The time in seconds at which to start playing the video.
240 | */
241 | start: PropTypes.number,
242 |
243 | // Player parameters
244 | /**
245 | * Pause this video automatically when another one plays.
246 | */
247 | autopause: PropTypes.bool,
248 |
249 | /**
250 | * Automatically start playback of the video. Note that this won’t work on
251 | * some devices.
252 | */
253 | autoplay: PropTypes.bool,
254 |
255 | /**
256 | * Show the byline on the video.
257 | */
258 | showByline: PropTypes.bool,
259 |
260 | /**
261 | * Specify the color of the video controls. Colors may be overridden by the
262 | * embed settings of the video. _(Ex: "ef2f9f")_
263 | */
264 | color: PropTypes.string,
265 |
266 | /**
267 | * Blocks the player from tracking any session data, including all cookies and analytics.
268 | */
269 | dnt: PropTypes.bool,
270 |
271 | // Player controls
272 | /**
273 | * Hide all elements in the player, such as the progress bar, sharing buttons, etc.
274 | * (requires Vimeo PRO / Business account)
275 | */
276 | controls: PropTypes.bool,
277 |
278 | /**
279 | * Play the video again when it reaches the end.
280 | */
281 | loop: PropTypes.bool,
282 |
283 | /**
284 | * Show the portrait on the video.
285 | */
286 | showPortrait: PropTypes.bool,
287 |
288 | /**
289 | * Show the title on the video.
290 | */
291 | showTitle: PropTypes.bool,
292 |
293 | /**
294 | * Starts in a muted state to help with autoplay
295 | */
296 | muted: PropTypes.bool,
297 |
298 | /**
299 | * Starts in a background state with no controls to help with autoplay
300 | */
301 | background: PropTypes.bool,
302 |
303 | /**
304 | * Enable responsive mode and resize according to parent element (experimental)
305 | */
306 | responsive: PropTypes.bool,
307 |
308 | /**
309 | * Specify playback rate (requires Vimeo PRO / Business account)
310 | */
311 | playbackRate: PropTypes.number,
312 |
313 | /**
314 | * Enable playback rate controls (requires Vimeo PRO / Business account)
315 | */
316 | speed: PropTypes.bool,
317 |
318 | /**
319 | * Allows for keyboard input to trigger player events.
320 | */
321 | keyboard: PropTypes.bool,
322 |
323 | /**
324 | * Show the picture-in-picture button in the controlbar
325 | * and enable the picture-in-picture API.
326 | */
327 | pip: PropTypes.bool,
328 |
329 | /**
330 | * Play video inline on mobile devices, to automatically
331 | * go fullscreen on playback set this parameter to false.
332 | */
333 | playsInline: PropTypes.bool,
334 |
335 | /**
336 | * Vimeo Plus, PRO, and Business members can default
337 | * an embedded video to a specific quality on desktop.
338 | */
339 | quality: PropTypes.string,
340 |
341 | /**
342 | * Turn captions/subtitles on for a specific language by default.
343 | */
344 | textTrack: PropTypes.string,
345 |
346 | /**
347 | * The responsive player and transparent background are enabled
348 | * by default, to disable set this parameter to false.
349 | */
350 | transparent: PropTypes.bool,
351 |
352 | // Events
353 | /* eslint-disable react/no-unused-prop-types */
354 |
355 | /**
356 | * Sent when the Vimeo player API has loaded.
357 | * Receives the Vimeo player object in the first parameter.
358 | */
359 | onReady: PropTypes.func,
360 | /**
361 | * Sent when the player triggers an error.
362 | */
363 | onError: PropTypes.func,
364 | /**
365 | * Triggered when video playback is initiated.
366 | */
367 | onPlay: PropTypes.func,
368 | /**
369 | * Triggered when the video starts playing.
370 | */
371 | onPlaying: PropTypes.func,
372 | /**
373 | * Triggered when the video pauses.
374 | */
375 | onPause: PropTypes.func,
376 | /**
377 | * Triggered any time the video playback reaches the end.
378 | * Note: when `loop` is turned on, the ended event will not fire.
379 | */
380 | onEnd: PropTypes.func,
381 | /**
382 | * Triggered as the `currentTime` of the video updates. It generally fires
383 | * every 250ms, but it may vary depending on the browser.
384 | */
385 | onTimeUpdate: PropTypes.func,
386 | /**
387 | * Triggered as the video is loaded. Reports back the amount of the video
388 | * that has been buffered.
389 | */
390 | onProgress: PropTypes.func,
391 | /**
392 | * Triggered when the player seeks to a specific time. An `onTimeUpdate`
393 | * event will also be fired at the same time.
394 | */
395 | onSeeked: PropTypes.func,
396 | /**
397 | * Triggered when the active text track (captions/subtitles) changes. The
398 | * values will be `null` if text tracks are turned off.
399 | */
400 | onTextTrackChange: PropTypes.func,
401 | /**
402 | * Triggered when the active cue for the current text track changes. It also
403 | * fires when the active text track changes. There may be multiple cues
404 | * active.
405 | */
406 | onCueChange: PropTypes.func,
407 | /**
408 | * Triggered when the current time hits a registered cue point.
409 | */
410 | onCuePoint: PropTypes.func,
411 | /**
412 | * Triggered when the volume in the player changes. Some devices do not
413 | * support setting the volume of the video independently from the system
414 | * volume, so this event will never fire on those devices.
415 | */
416 | onVolumeChange: PropTypes.func,
417 | /**
418 | * Triggered when the playback rate changes.
419 | */
420 | onPlaybackRateChange: PropTypes.func,
421 | /**
422 | * Triggered when a new video is loaded in the player.
423 | */
424 | onLoaded: PropTypes.func,
425 |
426 | /* eslint-enable react/no-unused-prop-types */
427 | };
428 | }
429 |
430 | Vimeo.defaultProps = {
431 | autopause: true,
432 | autoplay: false,
433 | showByline: true,
434 | controls: true,
435 | loop: false,
436 | showPortrait: true,
437 | showTitle: true,
438 | muted: false,
439 | background: false,
440 | responsive: false,
441 | dnt: false,
442 | speed: false,
443 | keyboard: true,
444 | pip: false,
445 | playsInline: true,
446 | transparent: true,
447 | };
448 |
449 | export default Vimeo;
450 |
--------------------------------------------------------------------------------