import React , { useRef , useImperativeHandle } from 'react' ; import useSetAdditionalAttributesOnVideo from '../hooks/useSetAdditionalAttributesOnVideo' ; import useIsHoveringOverVideo from '../hooks/useIsHoveringOverVideo' ; import useManageVideoPlayback from '../hooks/useManageVideoPlayback' ; import useUnloadVideo from '../hooks/useUnloadVideo' ; import useFormatVideoSrc from '../hooks/useFormatVideoSrc' ; import useFormatVideoCaptions from '../hooks/useFormatVideoCaptions' ; import { expandToFillContainerStyle , containerSizingStyles , pausedOverlayWrapperSizingStyles , videoSizingStyles , } from './HoverVideoPlayer.styles' ; import { OverlayState } from '../constants/OverlayState' ; import { HoverVideoPlayerProps } from '../HoverVideoPlayer.types' ; component license param { HoverVideoPlayerProps } props const HoverVideoPlayer = ( { videoSrc , videoCaptions = null , focused = false , disableDefaultEventHandling = false , hoverTarget = null , onHoverStart = null , onHoverEnd = null , hoverOverlay = null , pausedOverlay = null , loadingOverlay = null , loadingStateTimeout = 200 , overlayTransitionDuration = 400 , restartOnPaused = false , unloadVideoOnPaused = false , playbackRangeStart = null , playbackRangeEnd = null , muted = true , volume = 1 , loop = true , preload = null , crossOrigin = 'anonymous' , controls = false , controlsList = null , disableRemotePlayback = true , disablePictureInPicture = true , className = null , style = null , hoverOverlayWrapperClassName = null , hoverOverlayWrapperStyle = null , pausedOverlayWrapperClassName = null , pausedOverlayWrapperStyle = null , loadingOverlayWrapperClassName = null , loadingOverlayWrapperStyle = null , videoId = null , videoClassName = null , videoRef : forwardedVideoRef = null , videoStyle = null , sizingMode = 'video' , } ) : JSX . Element => { const containerRef = useRef ( null ) ; const videoRef = useRef < HTMLVideoElement > ( null ) ; useImperativeHandle ( forwardedVideoRef , ( ) => videoRef . current ) ; useSetAdditionalAttributesOnVideo ( videoRef , muted , volume , disableRemotePlayback , disablePictureInPicture ) ; const isHoveringOverVideo = useIsHoveringOverVideo ( hoverTarget || containerRef , disableDefaultEventHandling , onHoverStart , onHoverEnd ) ; const shouldPlayVideo = isHoveringOverVideo || focused ; const hasPausedOverlay = Boolean ( pausedOverlay ) ; const hasHoverOverlay = Boolean ( hoverOverlay ) ; const shouldWaitForOverlayTransitionBeforePausing = hasPausedOverlay || hasHoverOverlay ; const hasLoadingOverlay = Boolean ( loadingOverlay ) ; const [ overlayState , isVideoActive ] = useManageVideoPlayback ( videoRef , shouldPlayVideo , playbackRangeStart , playbackRangeEnd , loop , restartOnPaused , shouldWaitForOverlayTransitionBeforePausing , hasLoadingOverlay , overlayTransitionDuration , loadingStateTimeout ) ; const shouldUnloadVideo = unloadVideoOnPaused && ! isVideoActive ; useUnloadVideo ( videoRef , shouldUnloadVideo ) ; const formattedVideoSources = useFormatVideoSrc ( videoSrc , playbackRangeStart , playbackRangeEnd ) ; const formattedVideoCaptions = useFormatVideoCaptions ( videoCaptions ) ; const hasPlaybackRange = playbackRangeStart !== null || playbackRangeEnd !== null ; const isPausedOverlayVisible = overlayState !== OverlayState . playing ; const isLoadingOverlayVisibile = overlayState === OverlayState . loading ; return ( < div data - testid = "hover-video-player-container" ref = { containerRef } className = { className } style = { { ... containerSizingStyles [ sizingMode ] , position : 'relative' , ... style , } } > { hasPausedOverlay ? ( < div style = { { ... pausedOverlayWrapperSizingStyles [ sizingMode ] , zIndex : 1 , opacity : isPausedOverlayVisible ? 1 : 0 , transition : `opacity ${ overlayTransitionDuration }ms` , pointerEvents : isPausedOverlayVisible ? 'auto' : 'none' , ... pausedOverlayWrapperStyle , } } className = { pausedOverlayWrapperClassName } data - testid = "paused-overlay-wrapper" > { pausedOverlay } < /div>) : null} { hasLoadingOverlay ? ( < div style = { { ... expandToFillContainerStyle , zIndex : 2 , opacity : isLoadingOverlayVisibile ? 1 : 0 , transition : `opacity ${ overlayTransitionDuration }ms` , pointerEvents : isLoadingOverlayVisibile ? 'auto' : 'none' , ... loadingOverlayWrapperStyle , } } className = { loadingOverlayWrapperClassName } data - testid = "loading-overlay-wrapper" > { loadingOverlay } < /div>) : null} { hasHoverOverlay ? ( < div style = { { ... expandToFillContainerStyle , zIndex : 3 , opacity : shouldPlayVideo ? 1 : 0 , transition : `opacity ${ overlayTransitionDuration }ms` , pointerEvents : shouldPlayVideo ? 'auto' : 'none' , ... hoverOverlayWrapperStyle , } } className = { hoverOverlayWrapperClassName } data - testid = "hover-overlay-wrapper" > { hoverOverlay } < /div>) : null} { } < video loop = { loop && ! hasPlaybackRange } playsInline preload = { preload } crossOrigin = { crossOrigin } ref = { videoRef } style = { { ... videoSizingStyles [ sizingMode ] , objectFit : 'cover' , ... videoStyle , } } controls = { controls } controlsList = { controlsList } className = { videoClassName } id = { videoId } data - testid = "video-element" > { ! shouldUnloadVideo && formattedVideoSources . map ( ( { src , type } ) => ( < source key = { src } src = { src } type = { type } / > ) ) } { formattedVideoCaptions . map ( ( { src , srcLang , label , kind , default : isDefault } ) => ( < track key = { src } kind = { kind } src = { src } srcLang = { srcLang } label = { label } default = { isDefault } / > ) ) } < /video> < /div>); } ; export default HoverVideoPlayer ;