@shopgate/engage
Version:
Shopgate's ENGAGE library.
159 lines (157 loc) • 4.58 kB
JavaScript
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;