diff --git a/package.json b/package.json index 8b11bba5e8..c2e39f3dff 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "@emotion/react": "11.11.1", "@emotion/styled": "11.11.0", + "@vidstack/react": "^1.12.12", "core-js": "3.7.0", "lodash": "^4.17.5", "react": "18.3.1", @@ -86,6 +87,7 @@ "prettier": "^2.6.2", "react-test-renderer": "18.3.1", "storybook": "^8.3.6", + "style-loader": "^4.0.0", "svgo": "^1.3.2", "syncpack": "^10.9.3", "ts-jest": "29.1.1", diff --git a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap index 21c0132482..733311f713 100644 --- a/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap +++ b/packages/gamut/__tests__/__snapshots__/gamut.test.ts.snap @@ -123,5 +123,6 @@ exports[`Gamut Exported Keys 1`] = ` "useLocalQuery", "useSubmitState", "Video", + "VideoPlayer", ] `; diff --git a/packages/gamut/jest.config.ts b/packages/gamut/jest.config.ts index 4c754b1fbb..fcad1bb482 100644 --- a/packages/gamut/jest.config.ts +++ b/packages/gamut/jest.config.ts @@ -8,4 +8,5 @@ export default base('gamut', { setupFiles: ['/../../script/jest/base-setup.js'], setupFilesAfterEnv: ['/../../script/jest/rtl-setup.js'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + transformIgnorePatterns: ["node_modules/(?!(@vidstack/react)/)"], }); diff --git a/packages/gamut/src/VideoPlayer/index.tsx b/packages/gamut/src/VideoPlayer/index.tsx new file mode 100644 index 0000000000..8d5497c37b --- /dev/null +++ b/packages/gamut/src/VideoPlayer/index.tsx @@ -0,0 +1,90 @@ +/* eslint-disable gamut/no-css-standalone */ +import './styles/_vds-variables.scss'; + +import { MediaPlayer, MediaPlayerInstance, MediaProvider, Poster, Track, TrackProps } from '@vidstack/react'; +import { PlayerSrc, ThumbnailSrc } from '@vidstack/react/types/vidstack'; +import cx from 'classnames'; +import * as React from 'react'; +import { useState } from 'react'; + +import { useIsMounted } from '../utils'; +import { VideoLayout } from './lib/VideoLayout'; +import styles from './styles/index.module.scss'; + +export type VideoPlayerProps = { + autoplay?: boolean; + className?: string; + controls?: boolean; + height?: number; + loop?: boolean; + muted?: boolean; + onPlay?: () => void; + onReady?: (player: any) => void; + placeholderImage?: string; + videoTitle?: string; + videoUrl: PlayerSrc; + width?: number; + textTracks?: TrackProps[]; + thumbnails?: ThumbnailSrc; +}; + +export const VideoPlayer: React.FC = ({ + autoplay = false, + className, + controls = true, + loop = false, + muted = false, + onPlay, + onReady, + placeholderImage, + videoTitle, + videoUrl, + textTracks, + thumbnails, +}) => { + const [loading, setLoading] = useState(true); + const [adActive, setAdActive] = useState(false); + const player = React.useRef(null); + const isMounted = useIsMounted(); + + return ( +
+ {isMounted && ( + setLoading(false)} + onPlay={onPlay} + onCanPlay={onReady} + > + + {placeholderImage && ( + + )} + {textTracks?.map((track) => ( + + ))} + + + + )} +
+ ); +}; diff --git a/packages/gamut/src/VideoPlayer/lib/VideoLayout.tsx b/packages/gamut/src/VideoPlayer/lib/VideoLayout.tsx new file mode 100644 index 0000000000..ea221d9e15 --- /dev/null +++ b/packages/gamut/src/VideoPlayer/lib/VideoLayout.tsx @@ -0,0 +1,55 @@ +import { ViewIcon, ViewOffIcon } from '@codecademy/gamut-icons'; +import { useColorModes } from '@codecademy/gamut-styles'; +import { useMediaRemote } from '@vidstack/react'; +import { DefaultVideoLayout } from '@vidstack/react/player/layouts/default'; +import { ThumbnailSrc } from '@vidstack/react/types/vidstack'; + +import { IconButton } from '../../Button'; +import { customLayoutSlots } from './slots'; +import { customIcons } from './utils'; + +export type VideoLayoutProps = { + controls?: boolean; + thumbnails?: ThumbnailSrc; + adActive: boolean; + setAdActive: (active: boolean) => void; + }; + +export const VideoLayout: React.FC = ({ + controls, + thumbnails, + adActive, + setAdActive, +}) => { + const playerRemote = useMediaRemote(); + const [mode] = useColorModes(); + + return ( +