feat(BOOK-257): Added VideoPlayer component
Approved-by: Christel Westerberg Approved-by: Bianca Widstam
This commit is contained in:
179
packages/design-system/lib/components/VideoPlayer/index.tsx
Normal file
179
packages/design-system/lib/components/VideoPlayer/index.tsx
Normal file
@@ -0,0 +1,179 @@
|
||||
'use client'
|
||||
|
||||
import { cx, VariantProps } from 'class-variance-authority'
|
||||
import { useRef, useState, VideoHTMLAttributes } from 'react'
|
||||
|
||||
import { Lang, languages } from '@scandic-hotels/common/constants/language'
|
||||
import { FocalPoint } from '@scandic-hotels/common/utils/imageVault'
|
||||
import { useIntl } from 'react-intl'
|
||||
import { VideoPlayerButton } from './Button'
|
||||
import { variants } from './variants'
|
||||
import styles from './videoPlayer.module.css'
|
||||
|
||||
interface Caption {
|
||||
src: string
|
||||
srcLang: Lang
|
||||
isDefault: boolean
|
||||
}
|
||||
|
||||
interface VideoPlayerProps extends VariantProps<typeof variants> {
|
||||
src: string
|
||||
className?: string
|
||||
captions?: Caption[]
|
||||
focalPoint?: FocalPoint
|
||||
autoPlay?: boolean
|
||||
}
|
||||
|
||||
export function VideoPlayer({
|
||||
src,
|
||||
captions,
|
||||
focalPoint = { x: 50, y: 50 },
|
||||
className,
|
||||
variant = 'inline',
|
||||
autoPlay,
|
||||
}: VideoPlayerProps) {
|
||||
const intl = useIntl()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
const [isActivated, setIsActivated] = useState(
|
||||
(variant === 'hero' && (autoPlay ?? true)) || !!autoPlay
|
||||
)
|
||||
const [isPlaying, setIsPlaying] = useState(autoPlay ?? false)
|
||||
const [isMuted, setIsMuted] = useState(true)
|
||||
const defaultProps = getVideoPropsByVariant(variant, isActivated, autoPlay)
|
||||
|
||||
const classNames = variants({
|
||||
className,
|
||||
variant,
|
||||
})
|
||||
|
||||
function togglePlay() {
|
||||
const videoElement = videoRef.current
|
||||
if (videoElement) {
|
||||
if (variant === 'hero') {
|
||||
if (videoElement.paused) {
|
||||
videoElement.play()
|
||||
} else {
|
||||
videoElement.pause()
|
||||
}
|
||||
} else {
|
||||
setIsActivated(true)
|
||||
videoElement.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleMuteToggle() {
|
||||
const videoElement = videoRef.current
|
||||
if (videoElement) {
|
||||
const currentlyMuted = videoElement.muted
|
||||
videoElement.muted = !currentlyMuted
|
||||
setIsMuted(!currentlyMuted)
|
||||
}
|
||||
}
|
||||
|
||||
function handleVolumeChangeEvent(event: React.UIEvent<HTMLVideoElement>) {
|
||||
if (event.currentTarget.muted && !isMuted) {
|
||||
setIsMuted(true)
|
||||
} else if (!event.currentTarget.muted && isMuted) {
|
||||
setIsMuted(false)
|
||||
}
|
||||
}
|
||||
|
||||
const showPlayButton =
|
||||
variant === 'hero' || (variant === 'inline' && !isActivated)
|
||||
const showMuteButton = variant === 'inline' && isActivated
|
||||
|
||||
return (
|
||||
<div className={cx(classNames, { [styles.isActivated]: isActivated })}>
|
||||
<video
|
||||
ref={videoRef}
|
||||
className={styles.video}
|
||||
src={src}
|
||||
style={
|
||||
focalPoint
|
||||
? { objectPosition: `${focalPoint.x}% ${focalPoint.y}%` }
|
||||
: undefined
|
||||
}
|
||||
onPlay={() => setIsPlaying(true)}
|
||||
onPause={() => setIsPlaying(false)}
|
||||
onVolumeChange={handleVolumeChangeEvent}
|
||||
{...defaultProps}
|
||||
>
|
||||
{captions?.length
|
||||
? captions.map(({ src, srcLang, isDefault }) => (
|
||||
<track
|
||||
key={src}
|
||||
src={src}
|
||||
kind="captions"
|
||||
srcLang={srcLang}
|
||||
label={languages[srcLang] || srcLang}
|
||||
default={isDefault}
|
||||
/>
|
||||
))
|
||||
: null}
|
||||
</video>
|
||||
{showPlayButton ? (
|
||||
<VideoPlayerButton
|
||||
className={styles.playButton}
|
||||
onPress={togglePlay}
|
||||
iconName={isPlaying ? 'pause' : 'play_arrow'}
|
||||
ariaLabel={
|
||||
isPlaying
|
||||
? intl.formatMessage({
|
||||
id: 'videoPlayer.pause',
|
||||
defaultMessage: 'Pause video',
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: 'videoPlayer.play',
|
||||
defaultMessage: 'Play video',
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
{showMuteButton ? (
|
||||
<VideoPlayerButton
|
||||
className={styles.muteButton}
|
||||
onPress={handleMuteToggle}
|
||||
iconName={isMuted ? 'volume_off' : 'volume_up'}
|
||||
ariaLabel={
|
||||
isMuted
|
||||
? intl.formatMessage({
|
||||
id: 'videoPlayer.mute',
|
||||
defaultMessage: 'Mute video',
|
||||
})
|
||||
: intl.formatMessage({
|
||||
id: 'videoPlayer.unmute',
|
||||
defaultMessage: 'Unmute video',
|
||||
})
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function getVideoPropsByVariant(
|
||||
variant: VideoPlayerProps['variant'],
|
||||
isActive: boolean,
|
||||
autoPlay?: boolean
|
||||
): VideoHTMLAttributes<HTMLVideoElement> {
|
||||
switch (variant) {
|
||||
case 'hero':
|
||||
return {
|
||||
controls: false,
|
||||
controlsList: 'nodownload nofullscreen noremoteplayback',
|
||||
autoPlay: autoPlay ?? true,
|
||||
muted: true,
|
||||
loop: true,
|
||||
}
|
||||
case 'inline':
|
||||
default:
|
||||
return {
|
||||
controls: isActive,
|
||||
controlsList: 'nodownload noremoteplayback',
|
||||
autoPlay: autoPlay ?? isActive,
|
||||
muted: true,
|
||||
loop: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user