organism-react-video
Version:
328 lines (318 loc) • 9.3 kB
JavaScript
import _objectWithoutProperties from "reshow-runtime/es/helpers/objectWithoutProperties";
import _createClass from "reshow-runtime/es/helpers/createClass";
import _classCallCheck from "reshow-runtime/es/helpers/classCallCheck";
import _defineProperty from "reshow-runtime/es/helpers/defineProperty";
import _objectSpread from "reshow-runtime/es/helpers/objectSpread2";
var _excluded = ["videoId", "videoParams", "hostname", "onStateChange", "onClick", "onLoad"],
_excluded2 = ["loading", "onError"];
// @ts-check
import * as React from "react";
var {
useEffect,
useImperativeHandle,
forwardRef,
useRef,
useState
} = React;
import { IframeContainer } from "organism-react-iframe";
import { doc, win } from "win-doc";
import { KEYS } from "reshow-constant";
import { useRefUpdate } from "reshow-hooks";
import callfunc from "call-func";
import { getSN } from "get-random-id";
import ResponsiveVideo from "../organisms/ResponsiveVideo.mjs";
/**
* Iframe api
* https://www.youtube.com/iframe_api
* https://www.youtube.com/s/player/eff63141/www-widgetapi.vflset/www-widgetapi.js
*
* Play State
* https://developers.google.com/youtube/iframe_api_reference#Playback_status
*/
/**
* @typedef {import('organism-react-iframe').IframeContainerExpose} IframeContainerExpose
*/
/**
* @param {string} func
* @param {any[]=} args
* @param {string=} id
* @returns {string}
*/
import { jsx as _jsx } from "react/jsx-runtime";
var message = function message(func, args, id) {
return JSON.stringify({
event: "command",
id,
func,
args
});
};
var defaultVideoParams = {
autoplay: 1,
loop: 1,
showinfo: 0,
controls: 0,
rel: 0,
mute: 1,
modestbranding: 1,
iv_load_policy: 3,
enablejsapi: 1
};
/**
* @typedef {import("../organisms/ResponsiveVideo").ResponsiveVideoProps} ResponsiveVideoProps
*/
/**
* @typedef {object} YoutubeRWDExtendsProps
* @property {"eager"|"lazy"} [loading]
* @property {string} [videoId]
* @property {{[key: string]: any}} [videoParams]
* @property {string|boolean} [hostname]
* @property {boolean} [disable]
* @property {string} [id]
* @property {Function} [onStateChange]
* @property {Function} [onLoad]
* @property {Function} [onError]
*/
/**
* @typedef {ResponsiveVideoProps & YoutubeRWDExtendsProps} YoutubeRWDProps
*/
/**
* @param {YoutubeRWDExtendsProps} props
* @see https://developers.google.com/youtube/player_parameters
*/
var getYoutubeUrl = function getYoutubeUrl(_ref) {
var {
videoId,
videoParams,
hostname
} = _ref;
var nextVideoParams = _objectSpread({}, videoParams);
if (nextVideoParams["enablejsapi"] && hostname) {
nextVideoParams["origin"] = hostname;
}
if (nextVideoParams["loop"]) {
nextVideoParams.playlist = videoId;
}
var aParams = [];
KEYS(nextVideoParams).forEach(function (key) {
return aParams.push(key + "=" + encodeURIComponent(nextVideoParams[key]));
});
var src = "https://www.youtube.com/embed/" + videoId + "?" + aParams.join("&");
return src;
};
/**
* @interface
*/
var PlayerState = /*#__PURE__*/_createClass(function PlayerState() {
_classCallCheck(this, PlayerState);
/**
* @type number
*/
_defineProperty(this, "state", void 0);
/**
* @type boolean
*/
_defineProperty(this, "isPlaying", void 0);
});
/**
* @typedef {object} YoutubeRWDExpose
* @property {Function} restart
* @property {Function} pause
* @property {function():PlayerState} togglePlayback
* @property {function(string):void} postMessage
* @property {ExecPost} exec
* @property {function():HTMLIFrameElement|undefined} getIframe
* @property {function():PlayerState} getPlayerState
*/
/**
* @callback ExecPost
* @param {string} cmd
* @param {any[]=} args
*/
/**
* @param {YoutubeRWDProps} props
*/
var useYoutubeRWD = function useYoutubeRWD(props) {
var lastProps = useRefUpdate(props);
var _lastProps$current = lastProps.current,
{
videoId,
videoParams,
hostname: propsHostname,
onStateChange,
onClick,
onLoad
} = _lastProps$current,
restProps = _objectWithoutProperties(_lastProps$current, _excluded);
var lastPlayerState = useRef({
state: -1,
isPlaying: false
});
var lastMessageId = useRef(lastProps.current.id);
if (null == lastMessageId.current) {
lastMessageId.current = getSN("youtube-widget");
}
var thisVideoParams = useRefUpdate(_objectSpread(_objectSpread({}, defaultVideoParams), videoParams));
var lastHostname = /**@type React.RefObject<string|boolean|undefined>*/
useRef(undefined);
var lastIframe = /**@type {React.RefObject<IframeContainerExpose|null>}*/
useRef(null);
var [state, setState] = useState({
load: false
});
useEffect(function () {
var loc = doc().location;
var strLoc = loc.protocol + "//" + loc.hostname;
if (loc.port) {
strLoc += ":" + loc.port;
}
lastHostname.current = null == propsHostname ? strLoc : propsHostname;
setState({
load: true
});
var gWin = win();
gWin.addEventListener("message", handler.message);
return function () {
gWin.removeEventListener("message", handler.message);
};
}, []);
/**
* @type {YoutubeRWDExpose}
*/
var expose = {
restart: function restart() {
expose.exec("playVideo");
},
pause: function pause() {
expose.exec("pauseVideo");
},
togglePlayback: function togglePlayback() {
if (lastPlayerState.current.isPlaying) {
expose.pause();
} else {
expose.restart();
}
return lastPlayerState.current;
},
postMessage: function postMessage(message) {
var _lastIframe$current;
var iframe = (_lastIframe$current = lastIframe.current) === null || _lastIframe$current === void 0 ? void 0 : _lastIframe$current.getEl();
if (iframe) {
var _iframe$contentWindow;
var origin = /**@type string*/
lastHostname.current ? lastHostname.current : "*";
return (_iframe$contentWindow = iframe.contentWindow) === null || _iframe$contentWindow === void 0 ? void 0 : _iframe$contentWindow.window.postMessage(message, origin);
}
},
exec: function exec(cmd, args) {
var cmdMessage = message(cmd, args, lastMessageId.current);
return expose.postMessage(cmdMessage);
},
getIframe: function getIframe() {
var _lastIframe$current2;
return (_lastIframe$current2 = lastIframe.current) === null || _lastIframe$current2 === void 0 ? void 0 : _lastIframe$current2.getEl();
},
getPlayerState: function getPlayerState() {
return lastPlayerState.current;
}
};
var handler = {
listening: function listening() {
var message = JSON.stringify({
event: "listening",
channel: "widget",
id: lastMessageId.current
});
expose.postMessage(message);
expose.exec("addEventListener", ["onStateChange"]);
},
load: function load(/**@type Event*/e) {
if (thisVideoParams.current.autoplay) {
expose.restart();
}
handler.listening();
callfunc(lastProps.current.onLoad, [e]);
},
click: function click(/**@type React.MouseEvent*/e) {
callfunc(lastProps.current.onClick, [e]);
},
message: function message(/**@type MessageEvent*/e) {
var _data;
if (null == e) {
return null;
}
var data = e.data;
if ("string" === typeof data) {
try {
data = JSON.parse(e.data);
} catch (e) {}
}
if (((_data = data) === null || _data === void 0 ? void 0 : _data.id) === lastMessageId.current) {
var _data2;
var playerState = (_data2 = data) === null || _data2 === void 0 || (_data2 = _data2.info) === null || _data2 === void 0 ? void 0 : _data2.playerState;
if (null != playerState) {
var isPlaying = false;
switch (playerState) {
case 1:
case 3:
isPlaying = true;
break;
}
lastPlayerState.current = {
state: playerState,
isPlaying
};
}
callfunc(lastProps.current.onStateChange, [data]);
}
}
};
return {
expose,
handler,
nextProps: restProps,
lastIframe,
state,
src: state.load ? getYoutubeUrl({
videoId,
videoParams: thisVideoParams.current,
hostname: lastHostname.current
}) : undefined
};
};
/**
* @type React.ForwardRefExoticComponent<?, YoutubeRWDProps>
*/
var YoutubeRWD = forwardRef(function (props, ref) {
var {
expose,
handler,
nextProps,
lastIframe,
state,
src
} = useYoutubeRWD(props);
var {
loading,
onError
} = nextProps,
restProps = _objectWithoutProperties(nextProps, _excluded2);
useImperativeHandle(ref, function () {
return expose;
}, []);
if (!state.load) {
return null;
}
return /*#__PURE__*/_jsx(ResponsiveVideo, _objectSpread(_objectSpread({}, restProps), {}, {
onClick: handler.click,
children: /*#__PURE__*/_jsx(IframeContainer, {
loading: loading,
allow: "autoplay; fullscreen; encrypted-media",
src: src,
ref: lastIframe,
onLoad: handler.load,
onError: onError
})
}));
});
export default YoutubeRWD;