From 7d819dfcd97a545ee46e1a62a20e553896b6e92e Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Fri, 12 Sep 2025 06:34:00 -0700 Subject: [PATCH] feat(react): add fullscreen button component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement React fullscreen button with native React patterns: - useFullscreenButtonState hook for media store integration - useFullscreenButtonProps hook for element attribute management - renderFullscreenButton function for button rendering - Connected component via toConnectedComponent factory - Explicit enter/exit methods matching HTML implementation - Proper accessibility and data attributes Provides consistent fullscreen functionality across HTML and React platforms using shared state definition from media-store. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../react/src/components/FullscreenButton.tsx | 89 +++++++++++++++++++ packages/react/react/src/index.tsx | 3 +- 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 packages/react/react/src/components/FullscreenButton.tsx diff --git a/packages/react/react/src/components/FullscreenButton.tsx b/packages/react/react/src/components/FullscreenButton.tsx new file mode 100644 index 00000000..5b51d94e --- /dev/null +++ b/packages/react/react/src/components/FullscreenButton.tsx @@ -0,0 +1,89 @@ +import { + shallowEqual, + useMediaSelector, + useMediaStore, +} from '@vjs-10/react-media-store'; +import * as React from 'react'; +import { toConnectedComponent } from '../utils/component-factory'; +import { fullscreenButtonStateDefinition } from '@vjs-10/media-store'; + +export const useFullscreenButtonState = (_props: any) => { + const mediaStore = useMediaStore(); + /** @TODO Fix type issues with hooks (CJP) */ + const mediaState = useMediaSelector( + fullscreenButtonStateDefinition.stateTransform, + shallowEqual, + ); + + const methods = React.useMemo( + () => fullscreenButtonStateDefinition.createRequestMethods(mediaStore.dispatch), + [mediaStore], + ); + + return { + fullscreen: mediaState.fullscreen, + requestEnterFullscreen: methods.requestEnterFullscreen, + requestExitFullscreen: methods.requestExitFullscreen, + } as const; +}; + +export type useFullscreenButtonState = typeof useFullscreenButtonState; +export type FullscreenButtonState = ReturnType; + +export const useFullscreenButtonProps = ( + props: React.PropsWithChildren<{ [k: string]: any }>, + state: ReturnType, +) => { + const baseProps: Record = { + /** @TODO Need another state provider in core for i18n (CJP) */ + /** aria attributes/props */ + role: 'button', + ['aria-label']: state.fullscreen ? 'exit fullscreen' : 'enter fullscreen', + /** tooltip */ + ['data-tooltip']: state.fullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen', + /** external props spread last to allow for overriding */ + ...props, + }; + + // Handle boolean data attribute: present with empty string when true, absent when false + if (state.fullscreen) { + baseProps['data-fullscreen'] = ''; + } + + return baseProps; +}; + +export type useFullscreenButtonProps = typeof useFullscreenButtonProps; +type FullscreenButtonProps = ReturnType; + +export const renderFullscreenButton = ( + props: FullscreenButtonProps, + state: FullscreenButtonState, +) => { + return ( + + ); +}; + +export type renderFullscreenButton = typeof renderFullscreenButton; + +export const FullscreenButton = toConnectedComponent( + useFullscreenButtonState, + useFullscreenButtonProps, + renderFullscreenButton, + 'FullscreenButton', +); +export default FullscreenButton; \ No newline at end of file diff --git a/packages/react/react/src/index.tsx b/packages/react/react/src/index.tsx index 67a269f6..1c84c0d6 100644 --- a/packages/react/react/src/index.tsx +++ b/packages/react/react/src/index.tsx @@ -7,4 +7,5 @@ export { Video, MediaElementVideo } from './components/Video'; // New hook-style components export { PlayButton } from './components/PlayButton'; export { MuteButton } from './components/MuteButton'; -export { VolumeRange } from './components/VolumeRange'; \ No newline at end of file +export { VolumeRange } from './components/VolumeRange'; +export { FullscreenButton } from './components/FullscreenButton'; \ No newline at end of file