diff --git a/examples/default-provider/app/(default)/mp4-source/page.tsx b/examples/default-provider/app/(default)/mp4-source/page.tsx index d34bb3c..3f3e590 100644 --- a/examples/default-provider/app/(default)/mp4-source/page.tsx +++ b/examples/default-provider/app/(default)/mp4-source/page.tsx @@ -10,7 +10,7 @@ export default function Page() { <>
& Pla const showCustomBlur = isCustomPoster && blurDataURL !== asset?.blurDataURL; if (showGeneratedBlur || showCustomBlur) { + imgStyleProps.gridArea = '1 / 1'; imgStyleProps.width = '100%'; imgStyleProps.height = '100%'; imgStyleProps.color = 'transparent'; @@ -101,12 +102,18 @@ const DefaultPlayer = forwardRef & Pla } return ( - + {slottedPosterImg} & Pla ; -export type { MuxVideoProps }; +export type MuxVideoProps = Omit & Omit, 'autoPlay'>; + +export type NativeVideoProps = Omit, 'preload' | 'width' | 'height'>; export type MediaProps = TPlaybackId extends string ? MuxVideoProps & { playbackId?: TPlaybackId } @@ -16,10 +17,11 @@ let MuxVideo: React.LazyExoticComponent; let HlsVideo: React.LazyExoticComponent; let DashVideo: React.LazyExoticComponent; -const Media = forwardRef((props, forwardedRef) => { +const Media = forwardRef((props, forwardedRef) => { if (typeof props.playbackId === 'string') { - MuxVideo ??= lazy(() => import('./mux-video-react.js')); + MuxVideo ??= lazy(() => import('@mux/mux-video/react')); + // @ts-expect-error return ; } diff --git a/src/components/players/media/mux-video-react.tsx b/src/components/players/media/mux-video-react.tsx deleted file mode 100644 index a4cf395..0000000 --- a/src/components/players/media/mux-video-react.tsx +++ /dev/null @@ -1,115 +0,0 @@ -'use client'; - -import React from 'react'; -// Use the mux-video custom element for renditions and audio tracks support. -import MuxVideoElement, { Attributes as MuxVideoElementAttrs } from '@mux/mux-video'; -import { MuxMediaProps } from '@mux/playback-core'; -import { camelCase } from '../../utils.js'; - -type MuxVideoElementType = Element & { - getTemplateHTML?(attrs: Record): string; - shadowRootOptions?: { mode: 'open' | 'closed'; delegatesFocus: boolean }; -}; - -const Element = MuxVideoElement as unknown as MuxVideoElementType; - -export type MuxVideoProps = MuxMediaProps & Omit, 'autoPlay'>; - -const MuxVideo = React.forwardRef((allProps, ref) => { - let { children, suppressHydrationWarning, ...props } = allProps; - const elementRef = React.useRef(null); - - for (let name in props) { - if (name[0] === 'o' && name[1] === 'n') { - const useCapture = name.endsWith('Capture'); - const eventName = name.slice(2, useCapture ? name.length - 7 : undefined).toLowerCase(); - const callback = (props as MuxVideoProps)[name as keyof MuxVideoProps]; - - React.useEffect(() => { - const eventTarget = elementRef?.current; - if (!eventTarget || typeof callback !== 'function') return; - - eventTarget.addEventListener(eventName, callback, useCapture); - - return () => { - eventTarget.removeEventListener(eventName, callback, useCapture); - }; - }, [elementRef?.current, callback]); - } - } - - const attrs = propsToAttrs(props); - - // Only render the custom element template HTML on the server.. - // The custom element will render itself on the client. - if (typeof window === 'undefined' && Element?.getTemplateHTML && Element?.shadowRootOptions) { - const { mode, delegatesFocus } = Element.shadowRootOptions; - - const templateShadowRoot = React.createElement('template', { - shadowrootmode: mode, - shadowrootdelegatesfocus: delegatesFocus, - dangerouslySetInnerHTML: { - __html: Element.getTemplateHTML(attrs), - }, - }); - - children = [templateShadowRoot, children]; - } - - return React.createElement('mux-video', { - ...attrs, - ref: React.useCallback( - (node: HTMLElement) => { - elementRef.current = node; - if (typeof ref === 'function') { - ref(node); - } else if (ref !== null) { - ref.current = node; - } - }, - [ref] - ), - children, - suppressHydrationWarning, - }); -}); - -export default MuxVideo; - -const ReactPropToAttrNameMap: Record = { - className: 'class', - classname: 'class', - htmlFor: 'for', - viewBox: 'viewBox', -}; - -// Add mapping from MuxVideoElement prop names to attribute names. -// e.g. playbackId to playback-id -for (let [constant, attrName] of Object.entries(MuxVideoElementAttrs)) { - const propName = camelCase(constant); - ReactPropToAttrNameMap[propName] = attrName; -} - -function propsToAttrs(props = {}) { - let attrs: Record = {}; - for (let [propName, propValue] of Object.entries(props)) { - let attrName = toAttrName(propName, propValue); - if (attrName) attrs[attrName] = toAttrValue(propValue); - } - return attrs; -} - -function toAttrName(propName: string, propValue: unknown) { - if (ReactPropToAttrNameMap[propName]) return ReactPropToAttrNameMap[propName]; - if (typeof propValue == 'undefined') return undefined; - if (typeof propValue === 'boolean' && !propValue) return undefined; - if (propName.startsWith('on') && typeof propValue === 'function') return undefined; - if (/[A-Z]/.test(propName)) return propName.toLowerCase(); - return propName; -} - -function toAttrValue(propValue: unknown) { - if (typeof propValue === 'boolean') return ''; - if (Array.isArray(propValue)) return propValue.join(' '); - return propValue; -} diff --git a/tests/components/video.test.tsx b/tests/components/video.test.tsx index 2954f7d..45e3b02 100644 --- a/tests/components/video.test.tsx +++ b/tests/components/video.test.tsx @@ -27,6 +27,7 @@ test('renders native video without source', async () => { test('renders mux-video without UI with imported source', async () => { await import('@mux/mux-video'); + const wrapper = create(