UNPKG

@shopgate/engage

Version:
159 lines (157 loc) 4.58 kB
import React, { useEffect, useMemo, useState } from 'react'; import { makeStyles } from '@shopgate/engage/styles'; import { useReduceMotion } from '@shopgate/engage/a11y/hooks'; import { usePrevious, useAppEventOnReturnFromBackground } from '@shopgate/engage/core/hooks'; import { ConditionalWrapper, Link } from '@shopgate/engage/components'; import PropTypes from 'prop-types'; import { isHttpsUrl } from "../../helpers"; import { jsx as _jsx } from "react/jsx-runtime"; const useStyles = makeStyles()((_theme, { borderRadius }) => ({ root: { width: '100%', display: 'flex', overflow: 'hidden', borderRadius }, video: { // Add additional pixels to the width to prevent visible horizontal hairlines on some browsers width: 'calc(100% + 3px)', display: 'flex' }, banner: { position: 'absolute', width: '100%', height: '100%', objectFit: 'cover' }, bannerContainer: { position: 'absolute', height: '100%', top: 0, left: 0, justifyContent: 'center', alignItems: 'center' } })); /** * The WidgetVideo component is used to display a video in a widget. * @param {Object} props The component props. * @param {boolean} props.isBanner Whether the video is used in a banner. * @param {string} props.url The video URL. * @param {boolean} props.muted Whether the video is muted. * @param {boolean} props.loop Whether the video is looping. * @param {boolean} props.controls Whether the video controls are shown. * @param {boolean} props.autoplay Whether the video should autoplay. * @param {number} props.borderRadius The border radius value. * @param {string} props.link The link URL. * @returns {JSX.Element} */ const WidgetVideo = ({ isBanner, url, muted, loop, controls, autoplay, borderRadius, link }) => { const reduceMotion = useReduceMotion(); const autoplayValue = reduceMotion ? false : isBanner || autoplay; const { classes, cx } = useStyles({ borderRadius }); const [hasError, setHasError] = useState(false); const videoRef = React.useRef(null); const prevUrl = usePrevious(url); const isValidUrl = useMemo(() => url ? isHttpsUrl(url) : false, [url]); const showControls = useMemo(() => { if (link || isBanner) { // When a link is set we never show controls to avoid side effects due to two clickable areas. return false; } return !autoplayValue || reduceMotion ? true : controls; }, [autoplayValue, controls, isBanner, link, reduceMotion]); // Resume video playback when app returned from background useAppEventOnReturnFromBackground(() => { if (!videoRef.current || reduceMotion || !autoplayValue) { return; } videoRef.current.play(); }); useEffect(() => { if (!videoRef.current) { return; } if (reduceMotion) { // Pause playback when reduced motion settings changed after video was rendered videoRef.current.pause(); return; } if (autoplayValue) { videoRef.current.play(); } else { videoRef.current.pause(); videoRef.current.currentTime = 0; } }, [autoplayValue, reduceMotion]); useEffect(() => { if (!url || url !== prevUrl) { setHasError(false); } }, [hasError, prevUrl, url]); if (!url || hasError || !isValidUrl) return null; return /*#__PURE__*/_jsx("div", { className: cx(classes.root, { [classes.bannerContainer]: isBanner }), children: /*#__PURE__*/_jsx(ConditionalWrapper, { condition: !!link, wrapper: children => /*#__PURE__*/_jsx(Link, { href: link, children: children }), children: /*#__PURE__*/_jsx("video", { ref: videoRef // Set play position to 0.001s to guarantee that there is always a frame shown , src: `${url}#t=0.001`, muted: isBanner ? true : muted, controls: showControls, autoPlay: autoplayValue, className: cx(classes.video, { [classes.banner]: isBanner }), preload: "auto", playsInline: true, loop: isBanner ? true : loop, "aria-hidden": true, onError: () => { setHasError(true); }, children: /*#__PURE__*/_jsx("track", { kind: "captions", src: "", srcLang: "de", label: "Deutsch" }) }) }) }); }; WidgetVideo.defaultProps = { isBanner: false, link: null, url: null, muted: false, loop: false, controls: false, autoplay: false, borderRadius: 0 }; export default WidgetVideo;