Socket
Socket
Sign inDemoInstall

@u-wave/react-youtube

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@u-wave/react-youtube - npm Package Compare versions

Comparing version 0.7.2 to 1.0.0-alpha.0

.browserslistrc

21

.babelrc.js
module.exports = (api) => {
api.cache.never();
const isTest = api.caller((caller) => caller.name === '@babel/register');
const envOptions = {
modules: false,
loose: true,
};
if (process.env.NODE_ENV === 'cjs') {
envOptions.modules = 'commonjs';
}
if (process.env.NODE_ENV === 'test') {
envOptions.modules = 'commonjs';
envOptions.targets = { node: 'current' };
}
return {
targets: isTest ? { node: 'current' } : {},
presets: [
['@babel/env', envOptions],
['@babel/env', {
modules: isTest ? 'commonjs' : false,
}],
'@babel/react',

@@ -22,0 +11,0 @@ ],

module.exports = {
extends: 'airbnb',
parserOptions: {
ecmaVersion: 2021,
},
rules: {

@@ -10,2 +13,7 @@ // I disagree

'react/state-in-constructor': 'off',
// I disagree
'react/function-component-definition': ['error', {
namedComponents: 'function-declaration',
unnamedComponents: 'arrow-function',
}],
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],

@@ -12,0 +20,0 @@ 'jsx-a11y/label-has-for': ['error', {

@@ -7,2 +7,7 @@ # @u-wave/react-youtube change log

## 1.0.0-alpha.0 - 2021-12-01
* Use hooks internally.
* Drop support for React 16. This version requires React 17 or 18.
* Target evergreen browsers. If you need to support older browsers, you need to transpile this dependency.
## 0.7.2 - 2020-10-21

@@ -9,0 +14,0 @@ * Allow React 17 in peerDependency range.

@@ -1,2 +0,2 @@

import { createElement, Component } from 'react';
import * as React from 'react';
import PropTypes from 'prop-types';

@@ -8,5 +8,15 @@ import loadScript from 'load-script2';

subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
_setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _assertThisInitialized(self) {

@@ -317,3 +327,3 @@ if (self === void 0) {

style = _this$props3.style;
return /*#__PURE__*/createElement("div", {
return /*#__PURE__*/React.createElement("div", {
id: id,

@@ -327,3 +337,3 @@ className: className,

return YouTube;
}(Component);
}(React.Component);

@@ -552,2 +562,2 @@ if (process.env.NODE_ENV !== 'production') {

export default YouTube;
export { YouTube as default };

@@ -7,50 +7,38 @@ 'use strict';

var PropTypes = require('prop-types');
var loadScript = require('load-script2');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var PropTypes__default = /*#__PURE__*/_interopDefaultLegacy(PropTypes);
var loadScript__default = /*#__PURE__*/_interopDefaultLegacy(loadScript);
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
var eventNames = ['onReady', 'onStateChange', 'onPlaybackQualityChange', 'onPlaybackRateChange', 'onError', 'onApiChange'];
/* global window */
/* global window, document */
function loadSdk() {
return new Promise(function (resolve, reject) {
return new Promise((resolve, reject) => {
if (typeof window.YT === 'object' && typeof window.YT.ready === 'function') {
// A YouTube SDK is already loaded, so reuse that
window.YT.ready(function () {
resolve(window.YT);
});
window.YT.ready(resolve);
return;
}
loadScript__default['default']('https://www.youtube.com/iframe_api', function (err) {
if (err) {
reject(err);
} else {
window.YT.ready(function () {
resolve(window.YT);
});
}
});
const script = document.createElement('script');
script.async = true;
script.src = 'https://www.youtube.com/iframe_api';
script.onload = () => {
script.onerror = null;
script.onload = null;
window.YT.ready(resolve);
};
script.onerror = () => {
script.onerror = null;
script.onload = null;
reject(new Error('Could not load YouTube SDK'));
};
document.head.appendChild(script);
});
}
var sdk = null;
let sdk = null;
function getSdk() {

@@ -64,76 +52,117 @@ if (!sdk) {

var YouTube = /*#__PURE__*/function (_React$Component) {
_inheritsLoose(YouTube, _React$Component);
// @ts-check
const {
useCallback,
useEffect,
useRef,
useState
} = React__default["default"];
/**
* @template {keyof YT.Events} K
* @param {YT.Player} player
* @param {K} event
* @param {YT.Events[K]} handler
*/
function YouTube(props) {
var _this;
function useEventHandler(player, event, handler) {
useEffect(() => {
if (handler) {
player === null || player === void 0 ? void 0 : player.addEventListener(event, handler);
}
_this = _React$Component.call(this, props) || this;
_this.onPlayerReady = _this.onPlayerReady.bind(_assertThisInitialized(_this));
_this.onPlayerStateChange = _this.onPlayerStateChange.bind(_assertThisInitialized(_this));
_this.refContainer = _this.refContainer.bind(_assertThisInitialized(_this));
return _this;
}
return () => {
if (handler) {
player === null || player === void 0 ? void 0 : player.removeEventListener(event, handler);
}
};
}, [player, event, handler]);
}
/** @param {import('../index').YouTubeProps} props */
var _proto = YouTube.prototype;
_proto.componentDidMount = function componentDidMount() {
this.createPlayer();
};
function YouTube(_ref) {
let {
video,
id,
className,
style,
startSeconds,
endSeconds,
width,
height,
lang,
paused,
muted,
volume,
suggestedQuality,
playbackRate,
autoplay = false,
showCaptions = false,
controls = true,
disableKeyboard = false,
allowFullscreen = true,
annotations = true,
modestBranding = false,
playsInline = false,
showRelatedVideos = true,
showInfo = true,
onReady,
onError,
onStateChange,
onPlaybackQualityChange,
onPlaybackRateChange,
onCued = () => {},
onBuffering = () => {},
onPlaying = () => {},
onPause = () => {},
onEnd = () => {}
} = _ref;
_proto.componentDidUpdate = function componentDidUpdate(prevProps) {
var _this2 = this;
/** @type {React.RefObject<HTMLDivElement | null>} */
const container = useRef(null);
/** @type {React.MutableRefObject<() => YT.Player>} */
// eslint-disable-next-line react/destructuring-assignment
var changes = Object.keys(this.props).filter(function (name) {
return _this2.props[name] !== prevProps[name];
});
this.updateProps(changes);
};
const createPlayer = useRef(null);
const firstRender = useRef(false);
const [player, setPlayer] = useState(
/** @type {YT.Player | null} */
null);
/** @type {YT.PlayerVars} */
_proto.componentWillUnmount = function componentWillUnmount() {
if (this.playerInstance) {
this.playerInstance.destroy();
}
};
const playerVars = {
autoplay: autoplay ? 1 : 0,
cc_load_policy: showCaptions ? 1 : 0,
controls: controls ? 1 : 0,
disablekb: disableKeyboard ? 1 : 0,
fs: allowFullscreen ? 1 : 0,
hl: lang,
iv_load_policy: annotations ? 1 : 3,
start: startSeconds,
end: endSeconds,
modestbranding: modestBranding ? 1 : 0,
playsinline: playsInline ? 1 : 0,
rel: showRelatedVideos ? 1 : 0,
showinfo: showInfo ? 1 : 0
}; // Stick the player initialisation in a ref so it has the most recent props values
// when it gets instantiated.
_proto.onPlayerReady = function onPlayerReady(event) {
var _this$props = this.props,
volume = _this$props.volume,
muted = _this$props.muted,
suggestedQuality = _this$props.suggestedQuality,
playbackRate = _this$props.playbackRate;
if (typeof volume !== 'undefined') {
event.target.setVolume(volume * 100);
}
if (typeof muted !== 'undefined') {
if (muted) {
event.target.mute();
} else {
event.target.unMute();
if (!player) {
// eslint-disable-next-line no-undef
createPlayer.current = () => new YT.Player(container.current, {
videoId: video,
width,
height,
playerVars,
events: {
onReady: event => {
firstRender.current = true;
setPlayer(event.target);
}
}
}
});
}
if (typeof suggestedQuality !== 'undefined') {
event.target.setPlaybackQuality(suggestedQuality);
}
const handlePlayerStateChange = useCallback(event => {
const State = YT.PlayerState; // eslint-disable-line no-undef
if (typeof playbackRate !== 'undefined') {
event.target.setPlaybackRate(playbackRate);
}
this.resolvePlayer(event.target);
};
_proto.onPlayerStateChange = function onPlayerStateChange(event) {
var _this$props2 = this.props,
onCued = _this$props2.onCued,
onBuffering = _this$props2.onBuffering,
onPause = _this$props2.onPause,
onPlaying = _this$props2.onPlaying,
onEnd = _this$props2.onEnd;
var State = YT.PlayerState; // eslint-disable-line no-undef
switch (event.data) {

@@ -161,179 +190,98 @@ case State.CUED:

}
}
/**
* @private
*/
;
}, [onCued, onBuffering, onPause, onPlaying, onEnd]); // The effect that manages the player's lifetime.
_proto.getPlayerParameters = function getPlayerParameters() {
/* eslint-disable react/destructuring-assignment */
return {
autoplay: this.props.autoplay,
cc_load_policy: this.props.showCaptions ? 1 : 0,
controls: this.props.controls ? 1 : 0,
disablekb: this.props.disableKeyboard ? 1 : 0,
fs: this.props.allowFullscreen ? 1 : 0,
hl: this.props.lang,
iv_load_policy: this.props.annotations ? 1 : 3,
start: this.props.startSeconds,
end: this.props.endSeconds,
modestbranding: this.props.modestBranding ? 1 : 0,
playsinline: this.props.playsInline ? 1 : 0,
rel: this.props.showRelatedVideos ? 1 : 0,
showinfo: this.props.showInfo ? 1 : 0
};
/* eslint-enable react/destructuring-assignment */
}
/**
* @private
*/
;
_proto.getInitialOptions = function getInitialOptions() {
/* eslint-disable react/destructuring-assignment */
return {
videoId: this.props.video,
width: this.props.width,
height: this.props.height,
playerVars: this.getPlayerParameters(),
events: {
onReady: this.onPlayerReady,
onStateChange: this.onPlayerStateChange
useEffect(() => {
let instance = null;
let cancelled = false;
getSdk().then(() => {
if (!cancelled) {
instance = createPlayer.current();
}
};
/* eslint-enable react/destructuring-assignment */
}
/**
* @private
*/
;
_proto.updateProps = function updateProps(propNames) {
var _this3 = this;
this.player.then(function (player) {
propNames.forEach(function (name) {
// eslint-disable-next-line react/destructuring-assignment
var value = _this3.props[name];
switch (name) {
case 'muted':
if (value) {
player.mute();
} else {
player.unMute();
}
break;
case 'suggestedQuality':
player.setPlaybackQuality(value);
break;
case 'volume':
player.setVolume(value * 100);
break;
case 'paused':
if (value && player.getPlayerState() !== 2) {
player.pauseVideo();
} else if (!value && player.getPlayerState() === 2) {
player.playVideo();
}
break;
case 'id':
case 'className':
case 'width':
case 'height':
player.getIframe()[name] = value; // eslint-disable-line no-param-reassign
break;
case 'video':
if (!value) {
player.stopVideo();
} else {
var _this3$props = _this3.props,
startSeconds = _this3$props.startSeconds,
endSeconds = _this3$props.endSeconds,
autoplay = _this3$props.autoplay;
var opts = {
videoId: value,
startSeconds: startSeconds || 0,
endSeconds: endSeconds
};
if (autoplay) {
player.loadVideoById(opts);
} else {
player.cueVideoById(opts);
}
}
break;
}
});
});
}
/**
* @private
*/
;
return () => {
var _instance;
_proto.createPlayer = function createPlayer() {
var _this4 = this;
cancelled = true;
(_instance = instance) === null || _instance === void 0 ? void 0 : _instance.destroy();
};
}, []);
useEventHandler(player, 'onStateChange', handlePlayerStateChange);
useEventHandler(player, 'onReady', onReady);
useEventHandler(player, 'onStateChange', onStateChange);
useEventHandler(player, 'onPlaybackQualityChange', onPlaybackQualityChange);
useEventHandler(player, 'onPlaybackRateChange', onPlaybackRateChange);
useEventHandler(player, 'onError', onError);
useEffect(() => {
if (player) {
player.getIframe().width = width;
}
}, [player, width]);
useEffect(() => {
if (player) {
player.getIframe().height = height;
}
}, [player, height]);
useEffect(() => {
if (muted) {
player === null || player === void 0 ? void 0 : player.mute();
} else {
player === null || player === void 0 ? void 0 : player.unMute();
}
}, [player, muted]);
useEffect(() => {
player === null || player === void 0 ? void 0 : player.setPlaybackQuality(suggestedQuality);
}, [player, suggestedQuality]);
useEffect(() => {
player === null || player === void 0 ? void 0 : player.setPlaybackRate(playbackRate);
}, [player, playbackRate]);
useEffect(() => {
player === null || player === void 0 ? void 0 : player.setVolume(volume * 100);
}, [player, volume]);
useEffect(() => {
if (!player) {
return;
}
var volume = this.props.volume;
this.player = getSdk().then(function (YT) {
return new Promise(function (resolve) {
_this4.resolvePlayer = resolve;
var player = new YT.Player(_this4.container, _this4.getInitialOptions()); // Store the instance directly so we can destroy it sync in
// `componentWillUnmount`.
if (paused && player.getPlayerState() !== 2) {
player.pauseVideo();
} else if (!paused && player.getPlayerState() === 2) {
player.playVideo();
}
}, [player, paused]);
useEffect(() => {
if (!player) {
return;
} // Avoid calling a load() function when the player has just initialised,
// since it will already be up to date at that stage.
_this4.playerInstance = player;
eventNames.forEach(function (name) {
player.addEventListener(name, function (event) {
// eslint-disable-next-line react/destructuring-assignment
var handler = _this4.props[name];
if (handler) {
handler(event);
}
});
});
});
});
if (typeof volume === 'number') {
this.updateProps(['volume']);
if (firstRender.current) {
firstRender.current = false;
return;
}
}
/**
* @private
*/
;
_proto.refContainer = function refContainer(container) {
this.container = container;
};
if (!video) {
player.stopVideo();
} else {
const opts = {
videoId: video,
startSeconds: startSeconds || 0,
endSeconds
};
_proto.render = function render() {
var _this$props3 = this.props,
id = _this$props3.id,
className = _this$props3.className,
style = _this$props3.style;
return /*#__PURE__*/React.createElement("div", {
id: id,
className: className,
style: style,
ref: this.refContainer
});
};
if (autoplay) {
player.loadVideoById(opts);
} else {
player.cueVideoById(opts);
}
}
}, [player, video]);
return /*#__PURE__*/React__default["default"].createElement("div", {
id: id,
className: className,
style: style,
ref: container
});
}
return YouTube;
}(React.Component);
if (process.env.NODE_ENV !== 'production') {

@@ -344,3 +292,3 @@ YouTube.propTypes = {

*/
video: PropTypes__default['default'].string,
video: PropTypes__default["default"].string,

@@ -350,3 +298,3 @@ /**

*/
id: PropTypes__default['default'].string,
id: PropTypes__default["default"].string,

@@ -356,3 +304,3 @@ /**

*/
className: PropTypes__default['default'].string,
className: PropTypes__default["default"].string,

@@ -362,3 +310,3 @@ /**

*/
style: PropTypes__default['default'].object,
style: PropTypes__default["default"].object,
// eslint-disable-line react/forbid-prop-types

@@ -369,3 +317,3 @@

*/
width: PropTypes__default['default'].oneOfType([PropTypes__default['default'].number, PropTypes__default['default'].string]),
width: PropTypes__default["default"].oneOfType([PropTypes__default["default"].number, PropTypes__default["default"].string]),

@@ -375,3 +323,3 @@ /**

*/
height: PropTypes__default['default'].oneOfType([PropTypes__default['default'].number, PropTypes__default['default'].string]),
height: PropTypes__default["default"].oneOfType([PropTypes__default["default"].number, PropTypes__default["default"].string]),

@@ -381,3 +329,3 @@ /**

*/
paused: PropTypes__default['default'].bool,
paused: PropTypes__default["default"].bool,
// eslint-disable-line react/no-unused-prop-types

@@ -391,3 +339,3 @@ // Player parameters

*/
autoplay: PropTypes__default['default'].bool,
autoplay: PropTypes__default["default"].bool,

@@ -399,3 +347,3 @@ /**

*/
showCaptions: PropTypes__default['default'].bool,
showCaptions: PropTypes__default["default"].bool,

@@ -407,3 +355,3 @@ /**

*/
controls: PropTypes__default['default'].bool,
controls: PropTypes__default["default"].bool,

@@ -415,3 +363,3 @@ /**

*/
disableKeyboard: PropTypes__default['default'].bool,
disableKeyboard: PropTypes__default["default"].bool,

@@ -423,3 +371,3 @@ /**

*/
allowFullscreen: PropTypes__default['default'].bool,
allowFullscreen: PropTypes__default["default"].bool,

@@ -432,3 +380,3 @@ /**

*/
lang: PropTypes__default['default'].string,
lang: PropTypes__default["default"].string,

@@ -440,3 +388,3 @@ /**

*/
annotations: PropTypes__default['default'].bool,
annotations: PropTypes__default["default"].bool,

@@ -448,3 +396,3 @@ /**

*/
startSeconds: PropTypes__default['default'].number,
startSeconds: PropTypes__default["default"].number,

@@ -456,3 +404,3 @@ /**

*/
endSeconds: PropTypes__default['default'].number,
endSeconds: PropTypes__default["default"].number,

@@ -464,3 +412,3 @@ /**

*/
modestBranding: PropTypes__default['default'].bool,
modestBranding: PropTypes__default["default"].bool,

@@ -472,3 +420,3 @@ /**

*/
playsInline: PropTypes__default['default'].bool,
playsInline: PropTypes__default["default"].bool,

@@ -480,3 +428,3 @@ /**

*/
showRelatedVideos: PropTypes__default['default'].bool,
showRelatedVideos: PropTypes__default["default"].bool,

@@ -491,3 +439,3 @@ /**

*/
showInfo: PropTypes__default['default'].bool,
showInfo: PropTypes__default["default"].bool,

@@ -497,3 +445,3 @@ /**

*/
volume: PropTypes__default['default'].number,
volume: PropTypes__default["default"].number,

@@ -503,3 +451,3 @@ /**

*/
muted: PropTypes__default['default'].bool,
muted: PropTypes__default["default"].bool,

@@ -511,3 +459,3 @@ /**

*/
suggestedQuality: PropTypes__default['default'].string,
suggestedQuality: PropTypes__default["default"].string,

@@ -519,11 +467,9 @@ /**

*/
playbackRate: PropTypes__default['default'].number,
playbackRate: PropTypes__default["default"].number,
// Events
/* eslint-disable react/no-unused-prop-types */
/**
* Sent when the YouTube player API has loaded.
*/
onReady: PropTypes__default['default'].func,
onReady: PropTypes__default["default"].func,

@@ -533,3 +479,3 @@ /**

*/
onError: PropTypes__default['default'].func,
onError: PropTypes__default["default"].func,

@@ -539,3 +485,3 @@ /**

*/
onCued: PropTypes__default['default'].func,
onCued: PropTypes__default["default"].func,

@@ -545,3 +491,3 @@ /**

*/
onBuffering: PropTypes__default['default'].func,
onBuffering: PropTypes__default["default"].func,

@@ -551,3 +497,3 @@ /**

*/
onPlaying: PropTypes__default['default'].func,
onPlaying: PropTypes__default["default"].func,

@@ -557,3 +503,3 @@ /**

*/
onPause: PropTypes__default['default'].func,
onPause: PropTypes__default["default"].func,

@@ -563,8 +509,6 @@ /**

*/
onEnd: PropTypes__default['default'].func,
onStateChange: PropTypes__default['default'].func,
onPlaybackRateChange: PropTypes__default['default'].func,
onPlaybackQualityChange: PropTypes__default['default'].func
/* eslint-enable react/no-unused-prop-types */
onEnd: PropTypes__default["default"].func,
onStateChange: PropTypes__default["default"].func,
onPlaybackRateChange: PropTypes__default["default"].func,
onPlaybackQualityChange: PropTypes__default["default"].func
};

@@ -584,9 +528,10 @@ }

showInfo: true,
onCued: function onCued() {},
onBuffering: function onBuffering() {},
onPlaying: function onPlaying() {},
onPause: function onPause() {},
onEnd: function onEnd() {}
onCued: () => {},
onBuffering: () => {},
onPlaying: () => {},
onPause: () => {},
onEnd: () => {}
};
exports.default = YouTube;
exports["default"] = YouTube;
//# sourceMappingURL=react-youtube.js.map
{
"name": "@u-wave/react-youtube",
"description": "YouTube player component for React.",
"version": "0.7.2",
"version": "1.0.0-alpha.0",
"author": "Renée Kooi <renee@kooi.me>",

@@ -10,5 +10,4 @@ "bugs": {

"dependencies": {
"@types/react": "^16.9.17",
"@types/youtube": "0.0.39",
"load-script2": "^1.0.1",
"@types/react": "^17.0.0",
"@types/youtube": "0.0.46",
"prop-types": "^15.7.2"

@@ -23,5 +22,4 @@ },

"@rollup/plugin-babel": "^5.0.4",
"cross-env": "^7.0.0",
"eslint": "^7.4.0",
"eslint-config-airbnb": "^18.2.0",
"eslint": "^8.2.0",
"eslint-config-airbnb": "^19.0.0",
"eslint-plugin-import": "^2.22.0",

@@ -34,3 +32,3 @@ "eslint-plugin-jsx-a11y": "^6.3.1",

"min-react-env": "^1.0.1",
"mocha": "^8.0.1",
"mocha": "^9.0.0",
"prop-types-table": "^1.0.0",

@@ -42,3 +40,3 @@ "proxyquire": "^2.1.3",

"rollup": "^2.0.2",
"tsd": "^0.13.1"
"tsd": "^0.19.0"
},

@@ -54,6 +52,13 @@ "homepage": "https://github.com/u-wave/react-youtube#readme",

"license": "MIT",
"main": "dist/react-youtube.js",
"module": "dist/react-youtube.es.js",
"main": "./dist/react-youtube.js",
"module": "./dist/react-youtube.mjs",
"exports": {
".": {
"require": "./dist/react-youtube.js",
"import": "./dist/react-youtube.mjs"
}
},
"types": "index.d.ts",
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
"react": "^17.0.0 || ^18.0.0-0"
},

@@ -67,10 +72,17 @@ "repository": {

"docs": "prop-types-table src/index.js | md-insert README.md --header Props -i",
"example": "npm install && cd example && npm install",
"example": "npm run -w example build && npm run -w example start",
"prepare": "npm run build",
"test": "npm run test:types && npm run test:mocha && npm run test:lint",
"test:lint": "eslint --cache --fix .",
"test:mocha": "cross-env NODE_ENV=test mocha --require @babel/register test/*.js",
"test:types": "tsd"
"browserslist": "npx browserslist --mobile-to-desktop '> 0.5%, last 2 versions, Firefox ESR, not dead, not IE 11' > .browserslistrc",
"test": "npm run tsd && npm run tests-only && npm run lint",
"lint": "eslint --cache --fix .",
"tests-only": "mocha --require @babel/register test/*.js",
"tsd": "tsd"
},
"workspaces": {
"packages": [
".",
"./example"
]
},
"sideEffects": false
}

@@ -8,4 +8,13 @@ import babel from '@rollup/plugin-babel';

output: [
{ format: 'cjs', file: meta.main, exports: 'named' },
{ format: 'es', file: meta.module },
{
format: 'cjs',
file: meta.exports['.'].require,
exports: 'named',
sourcemap: true,
},
{
format: 'esm',
file: meta.exports['.'].import,
sourcemap: true,
},
],

@@ -12,0 +21,0 @@

@@ -1,69 +0,112 @@

import * as React from 'react';
// @ts-check
import React from 'react';
import PropTypes from 'prop-types';
import eventNames from './eventNames';
import loadSdk from './loadSdk';
class YouTube extends React.Component {
constructor(props) {
super(props);
const {
useCallback,
useEffect,
useRef,
useState,
} = React;
this.onPlayerReady = this.onPlayerReady.bind(this);
this.onPlayerStateChange = this.onPlayerStateChange.bind(this);
this.refContainer = this.refContainer.bind(this);
}
componentDidMount() {
this.createPlayer();
}
componentDidUpdate(prevProps) {
// eslint-disable-next-line react/destructuring-assignment
const changes = Object.keys(this.props).filter((name) => this.props[name] !== prevProps[name]);
this.updateProps(changes);
}
componentWillUnmount() {
if (this.playerInstance) {
this.playerInstance.destroy();
/**
* @template {keyof YT.Events} K
* @param {YT.Player} player
* @param {K} event
* @param {YT.Events[K]} handler
*/
function useEventHandler(player, event, handler) {
useEffect(() => {
if (handler) {
player?.addEventListener(event, handler);
}
}
return () => {
if (handler) {
player?.removeEventListener(event, handler);
}
};
}, [player, event, handler]);
}
onPlayerReady(event) {
const {
volume,
muted,
suggestedQuality,
playbackRate,
} = this.props;
/** @param {import('../index').YouTubeProps} props */
function YouTube({
video,
id,
className,
style,
startSeconds,
endSeconds,
width,
height,
lang,
paused,
muted,
volume,
suggestedQuality,
playbackRate,
autoplay = false,
showCaptions = false,
controls = true,
disableKeyboard = false,
allowFullscreen = true,
annotations = true,
modestBranding = false,
playsInline = false,
showRelatedVideos = true,
showInfo = true,
onReady,
onError,
onStateChange,
onPlaybackQualityChange,
onPlaybackRateChange,
onCued = () => {},
onBuffering = () => {},
onPlaying = () => {},
onPause = () => {},
onEnd = () => {},
}) {
/** @type {React.RefObject<HTMLDivElement | null>} */
const container = useRef(null);
/** @type {React.MutableRefObject<() => YT.Player>} */
const createPlayer = useRef(null);
const firstRender = useRef(false);
const [player, setPlayer] = useState(/** @type {YT.Player | null} */ (null));
if (typeof volume !== 'undefined') {
event.target.setVolume(volume * 100);
}
if (typeof muted !== 'undefined') {
if (muted) {
event.target.mute();
} else {
event.target.unMute();
}
}
if (typeof suggestedQuality !== 'undefined') {
event.target.setPlaybackQuality(suggestedQuality);
}
if (typeof playbackRate !== 'undefined') {
event.target.setPlaybackRate(playbackRate);
}
/** @type {YT.PlayerVars} */
const playerVars = {
autoplay: autoplay ? 1 : 0,
cc_load_policy: showCaptions ? 1 : 0,
controls: controls ? 1 : 0,
disablekb: disableKeyboard ? 1 : 0,
fs: allowFullscreen ? 1 : 0,
hl: lang,
iv_load_policy: annotations ? 1 : 3,
start: startSeconds,
end: endSeconds,
modestbranding: modestBranding ? 1 : 0,
playsinline: playsInline ? 1 : 0,
rel: showRelatedVideos ? 1 : 0,
showinfo: showInfo ? 1 : 0,
};
this.resolvePlayer(event.target);
// Stick the player initialisation in a ref so it has the most recent props values
// when it gets instantiated.
if (!player) {
// eslint-disable-next-line no-undef
createPlayer.current = () => new YT.Player(container.current, {
videoId: video,
width,
height,
playerVars,
events: {
onReady: (event) => {
firstRender.current = true;
setPlayer(event.target);
},
},
});
}
onPlayerStateChange(event) {
const {
onCued,
onBuffering,
onPause,
onPlaying,
onEnd,
} = this.props;
const handlePlayerStateChange = useCallback((event) => {
const State = YT.PlayerState; // eslint-disable-line no-undef

@@ -89,153 +132,107 @@ switch (event.data) {

}
}
}, [onCued, onBuffering, onPause, onPlaying, onEnd]);
/**
* @private
*/
getPlayerParameters() {
/* eslint-disable react/destructuring-assignment */
return {
autoplay: this.props.autoplay,
cc_load_policy: this.props.showCaptions ? 1 : 0,
controls: this.props.controls ? 1 : 0,
disablekb: this.props.disableKeyboard ? 1 : 0,
fs: this.props.allowFullscreen ? 1 : 0,
hl: this.props.lang,
iv_load_policy: this.props.annotations ? 1 : 3,
start: this.props.startSeconds,
end: this.props.endSeconds,
modestbranding: this.props.modestBranding ? 1 : 0,
playsinline: this.props.playsInline ? 1 : 0,
rel: this.props.showRelatedVideos ? 1 : 0,
showinfo: this.props.showInfo ? 1 : 0,
};
/* eslint-enable react/destructuring-assignment */
}
// The effect that manages the player's lifetime.
useEffect(() => {
let instance = null;
let cancelled = false;
/**
* @private
*/
getInitialOptions() {
/* eslint-disable react/destructuring-assignment */
return {
videoId: this.props.video,
width: this.props.width,
height: this.props.height,
playerVars: this.getPlayerParameters(),
events: {
onReady: this.onPlayerReady,
onStateChange: this.onPlayerStateChange,
},
loadSdk().then(() => {
if (!cancelled) {
instance = createPlayer.current();
}
});
return () => {
cancelled = true;
instance?.destroy();
};
/* eslint-enable react/destructuring-assignment */
}
}, []);
/**
* @private
*/
updateProps(propNames) {
this.player.then((player) => {
propNames.forEach((name) => {
// eslint-disable-next-line react/destructuring-assignment
const value = this.props[name];
switch (name) {
case 'muted':
if (value) {
player.mute();
} else {
player.unMute();
}
break;
case 'suggestedQuality':
player.setPlaybackQuality(value);
break;
case 'volume':
player.setVolume(value * 100);
break;
case 'paused':
if (value && player.getPlayerState() !== 2) {
player.pauseVideo();
} else if (!value && player.getPlayerState() === 2) {
player.playVideo();
}
break;
case 'id':
case 'className':
case 'width':
case 'height':
player.getIframe()[name] = value; // eslint-disable-line no-param-reassign
break;
case 'video':
if (!value) {
player.stopVideo();
} else {
const { startSeconds, endSeconds, autoplay } = this.props;
const opts = {
videoId: value,
startSeconds: startSeconds || 0,
endSeconds,
};
if (autoplay) {
player.loadVideoById(opts);
} else {
player.cueVideoById(opts);
}
}
break;
default:
// Nothing
}
});
});
}
useEventHandler(player, 'onStateChange', handlePlayerStateChange);
useEventHandler(player, 'onReady', onReady);
useEventHandler(player, 'onStateChange', onStateChange);
useEventHandler(player, 'onPlaybackQualityChange', onPlaybackQualityChange);
useEventHandler(player, 'onPlaybackRateChange', onPlaybackRateChange);
useEventHandler(player, 'onError', onError);
/**
* @private
*/
createPlayer() {
const { volume } = this.props;
useEffect(() => {
if (player) {
player.getIframe().width = width;
}
}, [player, width]);
this.player = loadSdk().then((YT) => new Promise((resolve) => {
this.resolvePlayer = resolve;
useEffect(() => {
if (player) {
player.getIframe().height = height;
}
}, [player, height]);
const player = new YT.Player(this.container, this.getInitialOptions());
// Store the instance directly so we can destroy it sync in
// `componentWillUnmount`.
this.playerInstance = player;
useEffect(() => {
if (muted) {
player?.mute();
} else {
player?.unMute();
}
}, [player, muted]);
eventNames.forEach((name) => {
player.addEventListener(name, (event) => {
// eslint-disable-next-line react/destructuring-assignment
const handler = this.props[name];
if (handler) {
handler(event);
}
});
});
}));
useEffect(() => {
player?.setPlaybackQuality(suggestedQuality);
}, [player, suggestedQuality]);
if (typeof volume === 'number') {
this.updateProps(['volume']);
useEffect(() => {
player?.setPlaybackRate(playbackRate);
}, [player, playbackRate]);
useEffect(() => {
player?.setVolume(volume * 100);
}, [player, volume]);
useEffect(() => {
if (!player) {
return;
}
}
if (paused && player.getPlayerState() !== 2) {
player.pauseVideo();
} else if (!paused && player.getPlayerState() === 2) {
player.playVideo();
}
}, [player, paused]);
/**
* @private
*/
refContainer(container) {
this.container = container;
}
useEffect(() => {
if (!player) {
return;
}
render() {
const { id, className, style } = this.props;
// Avoid calling a load() function when the player has just initialised,
// since it will already be up to date at that stage.
if (firstRender.current) {
firstRender.current = false;
return;
}
return (
<div
id={id}
className={className}
style={style}
ref={this.refContainer}
/>
);
}
if (!video) {
player.stopVideo();
} else {
const opts = {
videoId: video,
startSeconds: startSeconds || 0,
endSeconds,
};
if (autoplay) {
player.loadVideoById(opts);
} else {
player.cueVideoById(opts);
}
}
}, [player, video]);
return (
<div
id={id}
className={className}
style={style}
ref={container}
/>
);
}

@@ -390,3 +387,2 @@

// Events
/* eslint-disable react/no-unused-prop-types */

@@ -424,4 +420,2 @@ /**

onPlaybackQualityChange: PropTypes.func,
/* eslint-enable react/no-unused-prop-types */
};

@@ -428,0 +422,0 @@ }

@@ -1,3 +0,2 @@

/* global window */
import loadScript from 'load-script2';
/* global window, document */

@@ -8,17 +7,21 @@ function loadSdk() {

// A YouTube SDK is already loaded, so reuse that
window.YT.ready(() => {
resolve(window.YT);
});
window.YT.ready(resolve);
return;
}
loadScript('https://www.youtube.com/iframe_api', (err) => {
if (err) {
reject(err);
} else {
window.YT.ready(() => {
resolve(window.YT);
});
}
});
const script = document.createElement('script');
script.async = true;
script.src = 'https://www.youtube.com/iframe_api';
script.onload = () => {
script.onerror = null;
script.onload = null;
window.YT.ready(resolve);
};
script.onerror = () => {
script.onerror = null;
script.onload = null;
reject(new Error('Could not load YouTube SDK'));
};
document.head.appendChild(script);
});

@@ -25,0 +28,0 @@ }

@@ -28,4 +28,5 @@ import { createSpy } from 'expect';

addEventListener: createSpy().andCall((eventName, fn) => {
if (eventName === 'ready') fn();
if (eventName === 'onReady') fn({ target: playerMock });
}),
removeEventListener: createSpy(),
mute: createSpy(),

@@ -69,3 +70,6 @@ unMute: createSpy(),

'./loadSdk': {
default: () => Promise.resolve(sdkMock),
default() {
global.YT = sdkMock;
return Promise.resolve(sdkMock);
},
},

@@ -72,0 +76,0 @@ }).default;

@@ -8,2 +8,3 @@ /**

import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import env from 'min-react-env';

@@ -14,6 +15,10 @@ import createYouTube from './createYouTube';

const render = (initialProps) => {
async function render(initialProps) {
const { YouTube, sdkMock, playerMock } = createYouTube();
let component;
let resolveReady;
const readyPromise = new Promise((resolve) => {
resolveReady = resolve;
});
// Emulate changes to component.props using a container component's state

@@ -30,6 +35,11 @@ class Container extends React.Component {

const onReady = (event) => {
resolveReady();
props.onReady?.(event);
};
return (
<YouTube
ref={(youtube) => { component = youtube; }}
{...props}
onReady={onReady}
/>

@@ -42,11 +52,16 @@ );

const container = new Promise((resolve) => {
ReactDOM.render(<Container {...initialProps} ref={resolve} />, div);
if (ReactDOM.version.startsWith('18')) {
ReactDOM.createRoot(div).render(<Container {...initialProps} ref={resolve} />);
} else {
ReactDOM.render(<Container {...initialProps} ref={resolve} />, div);
}
});
await readyPromise;
function rerender(newProps) {
return container.then((wrapper) => new Promise((resolve) => {
wrapper.setState({ props: newProps }, () => {
Promise.resolve().then(resolve);
});
}));
async function rerender(newProps) {
const wrapper = await container;
act(() => {
wrapper.setState({ props: newProps });
});
}

@@ -58,11 +73,10 @@

return component.player.then(() => ({
return {
sdkMock,
playerMock,
component,
rerender,
unmount,
}));
};
};
}
export default render;

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc