Skip to content

Commit

Permalink
feat: add default video component, add custom controls (#662)
Browse files Browse the repository at this point in the history
* feat: add default video component, add custom controls
  • Loading branch information
Kyzyl-ool authored Nov 7, 2023
1 parent 55ece9f commit ad0d3b4
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 28 deletions.
3 changes: 1 addition & 2 deletions src/blocks/Media/__stories__/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,13 @@
"media": {
"light": {
"video": {
"type": "player",
"src": [
"https://storage.yandexcloud.net/cloud-www-assets/constructor/storybook/images/video_8-12_white.webm",
"https://storage.yandexcloud.net/cloud-www-assets/constructor/storybook/images/video_8-12_white.mp4",
"https://storage.yandexcloud.net/cloud-www-assets/constructor/storybook/images/video_8-12_white.png"
],
"autoplay": true,
"ariaLabel": "Video accessible name example",
"ariaLabel": "Video with autoplay and play/pause button",
"controls": "custom",
"customControlsOptions": {
"type": "with-play-pause-button",
Expand Down
9 changes: 9 additions & 0 deletions src/components/DefaultVideo/DefaultVideo.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import '../../../styles/variables.scss';

$block: '.#{$ns}default-video';

#{$block} {
width: 100%;
display: flex;
align-items: center;
}
112 changes: 112 additions & 0 deletions src/components/DefaultVideo/DefaultVideo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, {Fragment, useCallback, useImperativeHandle, useRef, useState} from 'react';

import {CustomControlsType, MediaVideoControlsType, MediaVideoProps} from '../../models';
import {block} from '../../utils';
import {getVideoTypesWithPriority} from '../Media/Video/utils';
import CustomBarControls from '../ReactPlayer/CustomBarControls';

import './DefaultVideo.scss';

const b = block('default-video');

type DefaultVideoRefType = HTMLVideoElement | undefined;

interface DefaultVideoProps {
video: MediaVideoProps;
qa?: string;
customBarControlsClassName?: string;
className?: string;
}

export const DefaultVideo = React.forwardRef<DefaultVideoRefType, DefaultVideoProps>(
(props, ref) => {
const {video, qa, customBarControlsClassName} = props;
const {controls, customControlsOptions, muted: initiallyMuted} = video;
const {
muteButtonShown,
positioning,
type: customControlsType,
} = customControlsOptions || {};
const [isPaused, setIsPaused] = useState(false);
const [isMuted, setIsMuted] = useState(initiallyMuted);
const videoRef = useRef<HTMLVideoElement>(null);

// one may not use this hook and work with `ref` variable only, but
// in this case one should support both function type and object type,
// according to ForwardedRef type.
// Currently used way with extra ref and useImperativeHandle is more
// convenient and allows us to work with object typed ref only,
// avoiding typeof ref === 'function' statements
useImperativeHandle(
ref,
() => {
if (!videoRef?.current) {
return undefined;
}

return videoRef.current;
},
[videoRef],
);
const onPlayToggle = useCallback(() => {
setIsPaused((value) => {
if (value) {
videoRef?.current?.play();
} else {
videoRef?.current?.pause();
}

return !value;
});
}, [videoRef]);
const onMuteToggle = useCallback(() => {
setIsMuted((value) => !value);
}, []);

const onClick = useCallback(() => {
if (customControlsType === CustomControlsType.WithPlayPauseButton) {
onPlayToggle();
}
}, [onPlayToggle, customControlsType]);

return (
<Fragment>
<video
disablePictureInPicture
playsInline
// @ts-ignore
// eslint-disable-next-line react/no-unknown-property
pip="false"
className={b()}
ref={videoRef}
preload="metadata"
muted={isMuted}
aria-label={video.ariaLabel}
onClick={onClick}
>
{getVideoTypesWithPriority(video.src).map(({src, type}, index) => (
<source key={index} src={src} type={type} data-qa={qa} />
))}
</video>

{controls === MediaVideoControlsType.Custom && (
<CustomBarControls
className={customBarControlsClassName}
type={customControlsType}
isPaused={isPaused}
onPlayClick={onPlayToggle}
muteButtonShown={muteButtonShown}
shown
positioning={positioning}
mute={{
isMuted: Boolean(isMuted),
changeMute: onMuteToggle,
}}
/>
)}
</Fragment>
);
},
);

DefaultVideo.displayName = 'DefaultVideo';
6 changes: 0 additions & 6 deletions src/components/Media/Video/Video.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ $block: '.#{$ns}media-component-video';
overflow: hidden;
}

&__item {
width: 100%;
display: flex;
align-items: center;
}

&__react-player {
display: flex;
position: relative;
Expand Down
23 changes: 3 additions & 20 deletions src/components/Media/Video/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import React, {useEffect, useMemo, useRef} from 'react';

import {MediaComponentVideoProps, MediaVideoType, PlayButtonProps, QAProps} from '../../../models';
import {block, getQaAttrubutes} from '../../../utils';
import {DefaultVideo} from '../../DefaultVideo/DefaultVideo';
import ReactPlayerBlock from '../../ReactPlayer/ReactPlayer';

import {getVideoTypesWithPriority} from './utils';

import './Video.scss';

const b = block('media-component-video');
Expand Down Expand Up @@ -130,27 +129,11 @@ const Video = (props: VideoAllProps) => {
style={{height}}
data-qa={qaAttributes.default}
>
<video
disablePictureInPicture
playsInline
// @ts-ignore
// eslint-disable-next-line react/no-unknown-property
pip="false"
className={b('item')}
ref={ref}
preload="metadata"
muted
aria-label={video.ariaLabel}
>
{getVideoTypesWithPriority(video.src).map(({src, type}, index) => (
<source key={index} src={src} type={type} data-qa={qaAttributes.source} />
))}
</video>
<DefaultVideo ref={ref} video={video} qa={qaAttributes.source} />
</div>
) : null;
}, [
video.src,
video.ariaLabel,
video,
hasVideoFallback,
videoClassName,
height,
Expand Down

0 comments on commit ad0d3b4

Please sign in to comment.