UNPKG

@dotconnor/grommet

Version:

focus on the essential experience

488 lines (421 loc) 16.6 kB
"use strict"; exports.__esModule = true; exports.Video = void 0; var _react = _interopRequireWildcard(require("react")); var _styledComponents = require("styled-components"); var _defaultProps = require("../../default-props"); var _Box = require("../Box"); var _Button = require("../Button"); var _Menu = require("../Menu"); var _Meter = require("../Meter"); var _Stack = require("../Stack"); var _Text = require("../Text"); var _utils = require("../../utils"); var _StyledVideo = require("./StyledVideo"); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } // Split the volume control into 6 segments. Empirically determined. var VOLUME_STEP = 0.166667; var formatTime = function formatTime(time) { var minutes = Math.round(time / 60); if (minutes < 10) { minutes = "0" + minutes; } var seconds = Math.round(time) % 60; if (seconds < 10) { seconds = "0" + seconds; } return minutes + ":" + seconds; }; var Video = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) { var alignSelf = _ref.alignSelf, autoPlay = _ref.autoPlay, children = _ref.children, _ref$controls = _ref.controls, controls = _ref$controls === void 0 ? 'over' : _ref$controls, gridArea = _ref.gridArea, loop = _ref.loop, margin = _ref.margin, messages = _ref.messages, mute = _ref.mute, _onDurationChange = _ref.onDurationChange, _onEnded = _ref.onEnded, _onPause = _ref.onPause, _onPlay = _ref.onPlay, _onTimeUpdate = _ref.onTimeUpdate, _onVolumeChange = _ref.onVolumeChange, rest = _objectWithoutPropertiesLoose(_ref, ["alignSelf", "autoPlay", "children", "controls", "gridArea", "loop", "margin", "messages", "mute", "onDurationChange", "onEnded", "onPause", "onPlay", "onTimeUpdate", "onVolumeChange"]); var theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _defaultProps.defaultProps.theme; var _useState = (0, _react.useState)([]), captions = _useState[0], setCaptions = _useState[1]; var _useState2 = (0, _react.useState)(), currentTime = _useState2[0], setCurrentTime = _useState2[1]; var _useState3 = (0, _react.useState)(), duration = _useState3[0], setDuration = _useState3[1]; var _useState4 = (0, _react.useState)(), percentagePlayed = _useState4[0], setPercentagePlayed = _useState4[1]; var _useState5 = (0, _react.useState)(false), playing = _useState5[0], setPlaying = _useState5[1]; var _useState6 = (0, _react.useState)(), scrubTime = _useState6[0], setScrubTime = _useState6[1]; var _useState7 = (0, _react.useState)(), volume = _useState7[0], setVolume = _useState7[1]; var _useState8 = (0, _react.useState)(false), hasPlayed = _useState8[0], setHasPlayed = _useState8[1]; var _useState9 = (0, _react.useState)(), interacting = _useState9[0], setInteracting = _useState9[1]; var _useState10 = (0, _react.useState)(), height = _useState10[0], setHeight = _useState10[1]; var _useState11 = (0, _react.useState)(), width = _useState11[0], setWidth = _useState11[1]; var containerRef = (0, _react.useRef)(); var scrubberRef = (0, _react.useRef)(); var videoRef = (0, _utils.useForwardedRef)(ref); // mute if needed (0, _react.useEffect)(function () { var video = videoRef.current; if (video && mute) video.muted = true; }, [mute, videoRef]); // when the video is first rendered, set state from it where needed (0, _react.useEffect)(function () { var video = videoRef.current; if (video) { // hide all captioning to start with var textTracks = video.textTracks; for (var i = 0; i < textTracks.length; i += 1) { textTracks[i].mode = 'hidden'; } setCurrentTime(video.currentTime); setPercentagePlayed(video.currentTime / video.duration * 100); setVolume(videoRef.current.volume); } }, [videoRef]); // turn off interacting after a while (0, _react.useEffect)(function () { var timer = setTimeout(function () { if (interacting && !(0, _utils.containsFocus)(containerRef.current)) { setInteracting(false); } }, 3000); return function () { return clearTimeout(timer); }; }, [interacting]); (0, _react.useLayoutEffect)(function () { var video = videoRef.current; if (video) { if (video.videoHeight) { // set the size based on the video aspect ratio var rect = video.getBoundingClientRect(); var ratio = rect.width / rect.height; var videoRatio = video.videoWidth / video.videoHeight; if (videoRatio > ratio) { var nextHeight = rect.width / videoRatio; if (nextHeight !== height) { setHeight(nextHeight); setWidth(undefined); } } else { var nextWidth = rect.height * videoRatio; if (nextWidth !== width) { setHeight(undefined); setWidth(nextWidth); } } } // remember the state of the text tracks for subsequent rendering var textTracks = video.textTracks; if (textTracks.length > 0) { if (textTracks.length === 1) { var active = textTracks[0].mode === 'showing'; if (!captions || !captions[0] || captions[0].active !== active) { setCaptions([{ active: active }]); } } else { var nextCaptions = []; var set = false; for (var i = 0; i < textTracks.length; i += 1) { var track = textTracks[i]; var _active = track.mode === 'showing'; nextCaptions.push({ label: track.label, active: _active }); if (!captions || !captions[i] || captions[i].active !== _active) { set = true; } } if (set) { setCaptions(nextCaptions); } } } } }, [captions, height, videoRef, width]); var play = (0, _react.useCallback)(function () { return videoRef.current.play(); }, [videoRef]); var pause = (0, _react.useCallback)(function () { return videoRef.current.pause(); }, [videoRef]); var scrub = (0, _react.useCallback)(function (event) { if (scrubberRef.current) { var scrubberRect = scrubberRef.current.getBoundingClientRect(); var percent = (event.clientX - scrubberRect.left) / scrubberRect.width; setScrubTime(duration * percent); } }, [duration]); var seek = (0, _react.useCallback)(function (event) { if (scrubberRef.current) { var scrubberRect = scrubberRef.current.getBoundingClientRect(); var percent = (event.clientX - scrubberRect.left) / scrubberRect.width; if (duration) videoRef.current.currentTime = duration * percent; } }, [duration, videoRef]); var louder = (0, _react.useCallback)(function () { videoRef.current.volume += VOLUME_STEP; }, [videoRef]); var quieter = (0, _react.useCallback)(function () { videoRef.current.volume -= VOLUME_STEP; }, [videoRef]); var showCaptions = function showCaptions(index) { var textTracks = videoRef.current.textTracks; for (var i = 0; i < textTracks.length; i += 1) { textTracks[i].mode = i === index ? 'showing' : 'hidden'; } }; var fullscreen = (0, _react.useCallback)(function () { var video = videoRef.current; if (video.requestFullscreen) { video.requestFullscreen(); } else if (video.msRequestFullscreen) { video.msRequestFullscreen(); } else if (video.mozRequestFullScreen) { video.mozRequestFullScreen(); } else if (video.webkitRequestFullscreen) { video.webkitRequestFullscreen(); } else { console.warn("This browser doesn't support fullscreen."); } }, [videoRef]); var controlsElement; if (controls) { var over = controls === 'over'; var background = over ? theme.video.controls && theme.video.controls.background || { color: 'background-back', opacity: 'strong', dark: true } : undefined; var iconColor = over && (theme.video.icons.color || 'text'); var formattedTime = formatTime(scrubTime || currentTime || duration); var Icons = { ClosedCaption: theme.video.icons.closedCaption, Configure: theme.video.icons.configure, FullScreen: theme.video.icons.fullScreen, Pause: theme.video.icons.pause, Play: theme.video.icons.play, ReduceVolume: theme.video.icons.reduceVolume, Volume: theme.video.icons.volume }; var captionControls = captions.map(function (caption) { return { icon: caption.label ? undefined : /*#__PURE__*/_react["default"].createElement(Icons.ClosedCaption, { color: iconColor }), label: caption.label, active: caption.active, onClick: function onClick() { return showCaptions(caption.active ? -1 : 0); } }; }); controlsElement = /*#__PURE__*/_react["default"].createElement(_StyledVideo.StyledVideoControls, { over: over, active: !hasPlayed || controls === 'below' || over && interacting, onBlur: function onBlur() { if (!(0, _utils.containsFocus)(containerRef.current)) setInteracting(false); } }, /*#__PURE__*/_react["default"].createElement(_Box.Box, { direction: "row", align: "center", justify: "between", background: background }, /*#__PURE__*/_react["default"].createElement(_Button.Button, { icon: playing ? /*#__PURE__*/_react["default"].createElement(Icons.Pause, { color: iconColor, a11yTitle: messages.pauseButton }) : /*#__PURE__*/_react["default"].createElement(Icons.Play, { color: iconColor, a11yTitle: messages.playButton }), hoverIndicator: "background", onClick: playing ? pause : play }), /*#__PURE__*/_react["default"].createElement(_Box.Box, { direction: "row", align: "center", flex: true }, /*#__PURE__*/_react["default"].createElement(_Box.Box, { flex: true }, /*#__PURE__*/_react["default"].createElement(_Stack.Stack, null, /*#__PURE__*/_react["default"].createElement(_Meter.Meter, { "aria-label": messages.progressMeter, background: over ? theme.video.scrubber && theme.video.scrubber.track && theme.video.scrubber.track.color || 'dark-3' : undefined, size: "full", thickness: "small", values: [{ value: percentagePlayed || 0 }] }), /*#__PURE__*/_react["default"].createElement(_StyledVideo.StyledVideoScrubber, { "aria-label": messages.scrubber, ref: scrubberRef, tabIndex: 0, role: "button", value: scrubTime ? Math.round(scrubTime / duration * 100) : undefined, onMouseMove: scrub, onMouseLeave: function onMouseLeave() { return setScrubTime(undefined); }, onClick: seek }))), /*#__PURE__*/_react["default"].createElement(_Box.Box, { pad: { horizontal: 'small' } }, /*#__PURE__*/_react["default"].createElement(_Text.Text, { margin: "none" }, formattedTime))), /*#__PURE__*/_react["default"].createElement(_Menu.Menu, { icon: /*#__PURE__*/_react["default"].createElement(Icons.Configure, { color: iconColor }), dropAlign: { bottom: 'top', right: 'right' }, dropBackground: background, messages: { openMenu: messages.openMenu, closeMenu: messages.closeMenu }, items: [{ icon: /*#__PURE__*/_react["default"].createElement(Icons.Volume, { color: iconColor, a11yTitle: messages.volumeUp }), onClick: volume <= 1 - VOLUME_STEP ? louder : undefined, close: false }, { icon: /*#__PURE__*/_react["default"].createElement(Icons.ReduceVolume, { color: iconColor, a11yTitle: messages.volumeDown }), onClick: volume >= VOLUME_STEP ? quieter : undefined, close: false }].concat(captionControls, [{ icon: /*#__PURE__*/_react["default"].createElement(Icons.FullScreen, { color: iconColor, a11yTitle: messages.fullScreen }), onClick: fullscreen }]) }))); } var mouseEventListeners; if (controls === 'over') { mouseEventListeners = { onMouseEnter: function onMouseEnter() { return setInteracting(true); }, onMouseMove: function onMouseMove() { return setInteracting(true); }, onTouchStart: function onTouchStart() { return setInteracting(true); } }; } var style; if (rest.fit === 'contain' && controls === 'over') { // constrain the size to fit the aspect ratio so the controls // overlap correctly if (width) { style = { width: width }; } else if (height) { style = { height: height }; } } return /*#__PURE__*/_react["default"].createElement(_StyledVideo.StyledVideoContainer, _extends({ ref: containerRef }, mouseEventListeners, { alignSelf: alignSelf, gridArea: gridArea, margin: margin, style: style }), /*#__PURE__*/_react["default"].createElement(_StyledVideo.StyledVideo, _extends({}, rest, { ref: videoRef, onDurationChange: function onDurationChange(event) { var video = videoRef.current; setDuration(video.duration); setPercentagePlayed(video.currentTime / video.duration * 100); if (_onDurationChange) _onDurationChange(event); }, onEnded: function onEnded(event) { setPlaying(false); if (_onEnded) _onEnded(event); }, onPause: function onPause(event) { setPlaying(false); if (_onPause) _onPause(event); }, onPlay: function onPlay(event) { setPlaying(true); setHasPlayed(true); if (_onPlay) _onPlay(event); }, onTimeUpdate: function onTimeUpdate(event) { var video = videoRef.current; setCurrentTime(video.currentTime); setPercentagePlayed(video.currentTime / video.duration * 100); if (_onTimeUpdate) _onTimeUpdate(event); }, onVolumeChange: function onVolumeChange(event) { setVolume(videoRef.current.volume); if (_onVolumeChange) _onVolumeChange(event); }, autoPlay: autoPlay || false, loop: loop || false }), children), controlsElement); }); Video.defaultProps = { messages: { closeMenu: 'close menu', fullScreen: 'full screen', progressMeter: 'video progress', scrubber: 'scrubber', openMenu: 'open menu', pauseButton: 'pause', playButton: 'play', volumeDown: 'volume down', volumeUp: 'volume up' } }; Video.displayName = 'Video'; var VideoDoc; if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line global-require VideoDoc = require('./doc').doc(Video); } var VideoWrapper = VideoDoc || Video; exports.Video = VideoWrapper;