diff --git a/packages/react/src/LazyVideo.tsx b/packages/react/src/LazyVideo/LazyVideoClient.tsx similarity index 72% rename from packages/react/src/LazyVideo.tsx rename to packages/react/src/LazyVideo/LazyVideoClient.tsx index b6ccda6..6a28759 100644 --- a/packages/react/src/LazyVideo.tsx +++ b/packages/react/src/LazyVideo/LazyVideoClient.tsx @@ -4,27 +4,28 @@ import { useInView } from 'react-intersection-observer' import { useMediaQueries } from '@react-hook/media-query' import { useEffect, type ReactElement, useRef, useCallback, type MutableRefObject } from 'react' -import type { LazyVideoProps } from './types/lazyVideoTypes'; -import { fillStyles, transparentGif } from './lib/styles' +import type { LazyVideoProps } from '../types/lazyVideoTypes'; +import { fillStyles, transparentGif } from '../lib/styles' -type VideoSourceProps = { - src: Required['src'] - videoLoader: LazyVideoProps['videoLoader'] +type LazyVideoClientProps = Omit & { + srcUrl?: string + mediaSrcs?: Record } -type ResponsiveVideoSourceProps = Pick, - 'src' | 'videoLoader' | 'sourceMedia' -> & { +type ResponsiveVideoSourceProps = { + mediaSrcs: Required['mediaSrcs'] videoRef: VideoRef } type VideoRef = MutableRefObject // An video rendered within a Visual that supports lazy loading -export default function LazyVideo({ - src, sourceMedia, videoLoader, +export default function LazyVideoClient({ + srcUrl, mediaSrcs, alt, fit, position, priority, noPoster, paused, -}: LazyVideoProps): ReactElement { +}: LazyVideoClientProps): ReactElement { // Make a ref to the video so it can be controlled const videoRef = useRef() @@ -67,11 +68,6 @@ export default function LazyVideo({ // Simplify logic for whether to load sources const shouldLoad = priority || inView - // Multiple media queries and a loader func are necessary for responsive - const useResponsiveSource = sourceMedia - && sourceMedia?.length > 1 - && !!videoLoader - // Render video tag return ( ) } -// Return a simple source element -function Source({ - src, videoLoader -}: VideoSourceProps): ReactElement | undefined { - let srcUrl - if (videoLoader) srcUrl = videoLoader({ src }) - else if (typeof src == 'string') srcUrl = src - if (!srcUrl) return - return () -} - // Switch the video asset depending on media queries function ResponsiveSource({ - src, videoLoader, sourceMedia, videoRef + mediaSrcs, videoRef }: ResponsiveVideoSourceProps): ReactElement | undefined { - // Prepare a hash of source URLs and their media query constraint in the - // style expected by useMediaQueries - const queries = Object.fromEntries(sourceMedia.map(media => { - const url = videoLoader({ src, media }) - return [url, media] - })) - // Find the src url that is currently active - const { matches } = useMediaQueries(queries) + const { matches } = useMediaQueries(mediaSrcs) const srcUrl = getFirstMatch(matches) // Reload the video since the source changed diff --git a/packages/react/src/LazyVideo/LazyVideoServer.tsx b/packages/react/src/LazyVideo/LazyVideoServer.tsx new file mode 100644 index 0000000..4269dd4 --- /dev/null +++ b/packages/react/src/LazyVideo/LazyVideoServer.tsx @@ -0,0 +1,64 @@ +import type { ReactElement } from 'react' +import type { LazyVideoProps } from '../types/lazyVideoTypes' +import LazyVideoClient from './LazyVideoClient' + +// This wrapper function exists to take Function props and make them +// serializable for the LazyVideoClient component, which is a Next.js style +// client component. +export default function LazyVideo( + props: LazyVideoProps +): ReactElement | undefined { + + // Destructure some props + const { + src, + sourceMedia, + videoLoader, + } = props + + // Multiple media queries and a loader func are necessary for responsive + const useResponsiveSource = sourceMedia + && sourceMedia?.length > 1 + && !!videoLoader + + // Vars that will be conditionally populated + let srcUrl, mediaSrcs + + // Prepare a hash of source URLs and their media query constraint in the + // style expected by useMediaQueries. + if (useResponsiveSource) { + const mediaSrcEntries = sourceMedia.map(media => { + const url = videoLoader({ src, media }) + return [url, media] + }) + // If the array ended up empty, abort + if (mediaSrcEntries.filter(([url]) => !!url).length == 0) return + + // Make the hash + mediaSrcs = Object.fromEntries(mediaSrcEntries) + + // Make a simple string src url + } else { + if (videoLoader) srcUrl = videoLoader({ src }) + else if (typeof src == 'string') srcUrl = src + if (!srcUrl) return // If no url could be built, abort + } + + // Render client component + return ( + + ) +} + diff --git a/packages/react/src/LazyVideo/index.ts b/packages/react/src/LazyVideo/index.ts new file mode 100644 index 0000000..d35c603 --- /dev/null +++ b/packages/react/src/LazyVideo/index.ts @@ -0,0 +1,3 @@ +// Export the server component as the defalut LazyVideo component +import LazyVideoServer from './LazyVideoServer' +export default LazyVideoServer diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 5329140..ada96cb 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,5 +1,5 @@ import ReactVisual from './ReactVisual' -import LazyVideo from './LazyVideo' +import LazyVideo from './LazyVideo/LazyVideoServer' import VisualWrapper from './VisualWrapper' import { collectDataAttributes } from './lib/attributes'