From a1de5ebae30e99e677cf77f685c601bb595144f1 Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Mon, 8 Sep 2025 17:20:19 -0700 Subject: [PATCH] refactor(react): consolidate Video component into single module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Merge connected, connected-with-defaults, and media-elements Video components - Create unified Video.tsx in main components directory alongside PlayButton/MuteButton - Preserve all existing functionality: media store connection, custom components, defaults - Maintain backwards compatibility with both Video and MediaElementVideo exports - Remove duplicate component directories and reduce architectural complexity - Simplify component structure while keeping TypeScript types and 'use client' directive This consolidation reduces the Video component from 3 separate files across 3 directories to a single, comprehensive module that's easier to understand and maintain. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- packages/react/react/src/components/Video.tsx | 125 ++++++++++++++++++ .../connected-with-defaults/Video.tsx | 87 ------------ .../react/src/components/connected/Video.tsx | 54 -------- .../src/components/media-elements/Video.tsx | 87 ------------ packages/react/react/src/index.tsx | 4 +- 5 files changed, 126 insertions(+), 231 deletions(-) create mode 100644 packages/react/react/src/components/Video.tsx delete mode 100644 packages/react/react/src/components/connected-with-defaults/Video.tsx delete mode 100644 packages/react/react/src/components/connected/Video.tsx delete mode 100644 packages/react/react/src/components/media-elements/Video.tsx diff --git a/packages/react/react/src/components/Video.tsx b/packages/react/react/src/components/Video.tsx new file mode 100644 index 00000000..da6d2992 --- /dev/null +++ b/packages/react/react/src/components/Video.tsx @@ -0,0 +1,125 @@ +'use client'; +import * as React from 'react'; +import { useMediaRef } from '@vjs-10/react-media-store'; +import { createMediaStateOwner } from '@vjs-10/media'; +import { + ElementType, + VideoHTMLAttributes, + DetailedHTMLProps, + PropsWithChildren, + CSSProperties, + Ref, + useImperativeHandle, + useRef, +} from 'react'; + +export type MuxVideoProps = { + 'playback-id'?: string; +}; + +type MediaStateOwner = NonNullable>[0]>; + +/** @TODO Improve type inference and narrowing/widening for different use cases (CJP) */ +type ComponentType = ElementType< + Omit< + DetailedHTMLProps, HTMLVideoElement>, + 'ref' + > & { ref: Ref } +>; + +// These are the first steps/WIP POC of decoupling the Media State Owner from the DOM. +// Note that everything will still work if you use: +// 1. an audio/video element directly +// 2. a custom element a la media-elements +type CreateMediaStateOwner = typeof createMediaStateOwner; +const useMediaStateOwner = ( + ref: Ref, + createMediaStateOwner: CreateMediaStateOwner /*, props? */ +) => { + const mediaStateOwnerRef = useRef(createMediaStateOwner(/* props? */)); + useImperativeHandle(ref, () => mediaStateOwnerRef.current, []); + /** @TODO Parameterize this (CJP) */ + type ComponentProps = DetailedHTMLProps< + VideoHTMLAttributes, + HTMLVideoElement + >; + return { + updateMediaElement( + mediaEl: HTMLMediaElement | null, + props: ComponentProps + ) { + // NOTE: The details here will almost definitely change for a less "bare bones"/"POC" implementation of Media State Owner impl. (CJP) + mediaStateOwnerRef.current.mediaElement = mediaEl ?? undefined; + mediaStateOwnerRef.current.src = props.src as string; + if (props.muted) { + mediaStateOwnerRef.current.muted = props.muted; + } + }, + }; +}; + +const DefaultVideoComponent: ElementType< + Omit< + DetailedHTMLProps, HTMLVideoElement>, + 'ref' + > & { ref: Ref } +> = ({ children, ref, ...props }) => { + const { updateMediaElement } = useMediaStateOwner(ref, createMediaStateOwner); + return ( + + ); +}; + +/** + * @description This is a "thin wrapper" around the media component whose primary responsibility is to wire up the element + * to the 's MediaStore for the media state. + * @param props - Identical to both a