UNPKG

organism-react-video

Version:
328 lines (318 loc) 9.3 kB
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;