Commit 34e3dba8 by Moorthy G

feat(responsive-video): add ResponsiveVideo component

parent 4e07b742
import type { Meta, StoryObj } from '@storybook/react';
import ResponsiveVideoComponent from './ResponsiveVideo';
const meta: Meta<typeof ResponsiveVideoComponent> = {
title: 'Shared/ResponsiveVideo',
component: ResponsiveVideoComponent,
parameters: {
layout: 'centered'
},
tags: ['autodocs']
};
export default meta;
type Story = StoryObj<typeof meta>;
export const ResponsiveVideo: Story = {
args: {
src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerFun.mp4',
srcSet: [
{
src: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4',
media: '(min-width: 640px)'
}
],
autoPlay: true,
controls: true,
muted: true
}
};
export const AutoPlayFailed: Story = {
args: {
src: 'broken.mp4',
autoPlay: true,
autoPlayFallback: (
<h3 className="font-bold text-lg text-light bg-primary px-4 py-2">
Auto play failed. I am a fallback element
</h3>
)
}
};
'use client';
import React, { useState, useEffect, useRef } from 'react';
import breakpoints from './breakpoints';
export interface ResponsiveVideoProps
extends React.VideoHTMLAttributes<HTMLVideoElement> {
src: string;
/** first match in the array will be selected
* media should be a valid media query or
* a key from breakpoints object
*/
srcSet?: {
src: string;
media: string;
}[];
/** auto plays video source */
autoPlay?: boolean;
/** If autoplay fails and if there is a fallback,
* display this fallback element */
autoPlayFallback?: React.ReactNode;
}
const ResponsiveVideo = ({
src,
srcSet,
autoPlay,
autoPlayFallback,
...rest
}: ResponsiveVideoProps) => {
const [isAutoPlayFailed, setIsAutoPlayFailed] = useState(false);
const [currentSrc, setCurrentSrc] = useState(src);
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
async function play() {
try {
autoPlay && (await videoRef?.current?.play());
} catch (err) {
setIsAutoPlayFailed(true);
}
}
play();
}, [autoPlay, currentSrc]);
useEffect(() => {
const handleResize = () => {
const matchedSet = srcSet?.find(
({ media }) => window.matchMedia(breakpoints[media] || media).matches
);
setCurrentSrc(matchedSet?.src || src);
};
window.addEventListener('resize', handleResize);
handleResize();
return () => window.removeEventListener('resize', handleResize);
}, [src, srcSet]);
return isAutoPlayFailed && autoPlayFallback ? (
autoPlayFallback
) : (
<video ref={videoRef} src={currentSrc} {...rest} />
);
};
export default ResponsiveVideo;
type Breakpoints = {
[key: string]: string;
};
const breakpoints: Breakpoints = {
sm: '(min-width: 640px)',
md: '(min-width: 768px)',
lg: '(min-width: 1024px)',
xl: '(min-width: 1280px)',
xxl: '(min-width: 1536px)'
};
export default breakpoints;
export * from './ResponsiveVideo';
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment