From 75c6cb6bc3823816ba0ea07f9be46c3bc2e78424 Mon Sep 17 00:00:00 2001 From: Christian Pillsbury Date: Mon, 8 Sep 2025 19:44:01 -0700 Subject: [PATCH] refactor(react): restructure VolumeRange to use render function pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor VolumeRange component to follow the same architectural pattern as PlayButton and MuteButton components for consistency and maintainability. Changes: - Split component into useVolumeRangeState, useVolumeRangeProps, renderVolumeRange - Use toConnectedComponent factory for consistent component creation - Separate state management, props transformation, and rendering concerns - Add proper TypeScript exports for all hooks and types - Maintain existing functionality while improving code organization This ensures architectural consistency across all React media components and makes the codebase easier to maintain and extend. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../react/src/components/VolumeRange.tsx | 89 +++++++++++++------ 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/packages/react/react/src/components/VolumeRange.tsx b/packages/react/react/src/components/VolumeRange.tsx index 39fbbdc9..cfddc96a 100644 --- a/packages/react/react/src/components/VolumeRange.tsx +++ b/packages/react/react/src/components/VolumeRange.tsx @@ -1,24 +1,15 @@ -import React from 'react'; import { shallowEqual, useMediaSelector, useMediaStore, } from '@vjs-10/react-media-store'; +import * as React from 'react'; +import { toConnectedComponent } from '../utils/component-factory'; import { volumeRangeStateDefinition } from '@vjs-10/media-store'; -interface VolumeRangeProps { - className?: string; - style?: React.CSSProperties; -} - -export const VolumeRange: React.FC = ({ - className, - style, - ...props -}) => { +export const useVolumeRangeState = (_props: any) => { const mediaStore = useMediaStore(); - - // Use useMediaSelector to properly subscribe to state changes + /** @TODO Fix type issues with hooks (CJP) */ const mediaState = useMediaSelector( volumeRangeStateDefinition.stateTransform, shallowEqual, @@ -29,27 +20,69 @@ export const VolumeRange: React.FC = ({ [mediaStore], ); - const handleChange = (e: React.ChangeEvent) => { - methods.requestVolumeChange(parseFloat(e.target.value)); + return { + volume: mediaState.volume, + muted: mediaState.muted, + volumeLevel: mediaState.volumeLevel, + requestVolumeChange: methods.requestVolumeChange, + } as const; +}; + +export type useVolumeRangeState = typeof useVolumeRangeState; +export type VolumeRangeState = ReturnType; + +export const useVolumeRangeProps = ( + props: React.PropsWithChildren<{ [k: string]: any }>, + state: ReturnType, +) => { + const displayValue = state.muted ? 0 : state.volume; + + const baseProps: Record = { + /** @TODO These should probably be defined in the render function (CJP) */ + /** input properties */ + type: 'range', + min: '0', + max: '1', + step: '0.01', + value: displayValue, + /** aria attributes/props */ + 'aria-label': 'Volume', + 'aria-valuetext': `${Math.round(displayValue * 100)}%`, + /** data attributes */ + 'data-muted': state.muted, + 'data-volume-level': state.volumeLevel, + /** external props spread last to allow for overriding */ + ...props, }; - const displayValue = mediaState.muted ? 0 : mediaState.volume; + return baseProps; +}; +export type useVolumeRangeProps = typeof useVolumeRangeProps; +type VolumeRangeProps = ReturnType; + +export const renderVolumeRange = ( + props: VolumeRangeProps, + state: VolumeRangeState, +) => { return ( ) => { + /** @ts-ignore */ + if (props.disabled) return; + state.requestVolumeChange(parseFloat(e.target.value)); + }} /> ); }; + +export type renderVolumeRange = typeof renderVolumeRange; + +export const VolumeRange = toConnectedComponent( + useVolumeRangeState, + useVolumeRangeProps, + renderVolumeRange, + 'VolumeRange', +); +export default VolumeRange;