mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
refactor(react): create shared component factory for reusable architecture
- Extract toConnectedComponent into shared utility with full TypeScript generics - Create src/utils/component-factory.tsx with generic types: - StateHookFn, PropsHookFn, RenderFn for type safety - Generic toConnectedComponent factory function - ConnectedComponent type helper for inference - Update PlayButton and MuteButton to use shared factory - Remove duplicate toConnectedComponent implementations - Add proper JSDoc documentation for the component factory pattern - Fix TypeScript indexing issues with Record<string, any> typing - Maintains full type safety while eliminating code duplication This follows Adobe React Spectrum and Base UI architectural patterns for maximum reusability and extensibility across components. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
committed by
Christian Pillsbury
co-authored by
Claude
parent
26998a1217
commit
38215df70b
@@ -1,5 +1,6 @@
|
||||
import { useMediaDispatch, useMediaSelector } from '@vjs-10/react-media-store';
|
||||
import * as React from 'react';
|
||||
import { toConnectedComponent } from '../utils/component-factory';
|
||||
|
||||
export const useMuteButtonState = (_props: any) => {
|
||||
/** @TODO Fix type issues with hooks (CJP) */
|
||||
@@ -31,7 +32,7 @@ export const useMuteButtonProps = (
|
||||
props: React.PropsWithChildren<{ [k: string]: any }>,
|
||||
state: ReturnType<typeof useMuteButtonState>,
|
||||
) => {
|
||||
const baseProps = {
|
||||
const baseProps: Record<string, any> = {
|
||||
/** data attributes/props - non-boolean */
|
||||
['data-volume-level']: state.volumeLevel,
|
||||
/** @TODO Need another state provider in core for i18n (CJP) */
|
||||
@@ -79,25 +80,6 @@ export const renderMuteButton = (
|
||||
|
||||
export type renderMuteButton = typeof renderMuteButton;
|
||||
|
||||
export const toConnectedComponent = (
|
||||
useStateHook: useMuteButtonState,
|
||||
usePropsHook: useMuteButtonProps,
|
||||
defaultRender: renderMuteButton,
|
||||
displayName: string,
|
||||
) => {
|
||||
const ConnectedComponent = ({
|
||||
render = defaultRender,
|
||||
...props
|
||||
}: MuteButtonProps & { render: renderMuteButton }) => {
|
||||
const connectedState = useStateHook(props);
|
||||
const connectedProps = usePropsHook(props, connectedState);
|
||||
return render(connectedProps, connectedState);
|
||||
};
|
||||
|
||||
ConnectedComponent.displayName = displayName;
|
||||
return ConnectedComponent;
|
||||
};
|
||||
|
||||
export const MuteButton = toConnectedComponent(
|
||||
useMuteButtonState,
|
||||
useMuteButtonProps,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useMediaDispatch, useMediaSelector } from '@vjs-10/react-media-store';
|
||||
import * as React from 'react';
|
||||
import { toConnectedComponent } from '../utils/component-factory';
|
||||
|
||||
export const usePlayButtonState = (_props: any) => {
|
||||
/** @TODO Fix type issues with hooks (CJP) */
|
||||
@@ -29,7 +30,7 @@ export const usePlayButtonProps = (
|
||||
props: React.PropsWithChildren<{ [k: string]: any }>,
|
||||
state: ReturnType<typeof usePlayButtonState>,
|
||||
) => {
|
||||
const baseProps = {
|
||||
const baseProps: Record<string, any> = {
|
||||
/** @TODO Need another state provider in core for i18n (CJP) */
|
||||
/** aria attributes/props */
|
||||
role: 'button',
|
||||
@@ -75,25 +76,6 @@ export const renderPlayButton = (
|
||||
|
||||
export type renderPlayButton = typeof renderPlayButton;
|
||||
|
||||
export const toConnectedComponent = (
|
||||
useStateHook: usePlayButtonState,
|
||||
usePropsHook: usePlayButtonProps,
|
||||
defaultRender: renderPlayButton,
|
||||
displayName: string,
|
||||
) => {
|
||||
const ConnectedComponent = ({
|
||||
render = defaultRender,
|
||||
...props
|
||||
}: PlayButtonProps & { render?: renderPlayButton }) => {
|
||||
const connectedState = useStateHook(props);
|
||||
const connectedProps = usePropsHook(props, connectedState);
|
||||
return render(connectedProps, connectedState);
|
||||
};
|
||||
|
||||
ConnectedComponent.displayName = displayName;
|
||||
return ConnectedComponent;
|
||||
};
|
||||
|
||||
export const PlayButton = toConnectedComponent(
|
||||
usePlayButtonState,
|
||||
usePlayButtonProps,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import * as React from 'react';
|
||||
|
||||
/**
|
||||
* Generic types for the component factory pattern
|
||||
*/
|
||||
export type StateHookFn<TProps = any, TState = any> = (props: TProps) => TState;
|
||||
|
||||
export type PropsHookFn<TProps = any, TState = any, TResultProps = any> = (
|
||||
props: TProps,
|
||||
state: TState,
|
||||
) => TResultProps;
|
||||
|
||||
export type RenderFn<TProps = any, TState = any> = (
|
||||
props: TProps,
|
||||
state: TState,
|
||||
) => React.ReactElement;
|
||||
|
||||
/**
|
||||
* Generic factory function to create connected components following the hooks pattern
|
||||
* inspired by Adobe React Spectrum and Base UI architectures.
|
||||
*
|
||||
* @param useStateHook - Hook that provides component state
|
||||
* @param usePropsHook - Hook that enhances props with state-derived values
|
||||
* @param defaultRender - Default render function for the component
|
||||
* @param displayName - Display name for React DevTools
|
||||
* @returns Connected component with customizable render prop
|
||||
*/
|
||||
export const toConnectedComponent = <
|
||||
TProps extends Record<string, any>,
|
||||
TState,
|
||||
TResultProps extends Record<string, any>,
|
||||
TRenderFn extends RenderFn<TResultProps, TState>
|
||||
>(
|
||||
useStateHook: StateHookFn<TProps, TState>,
|
||||
usePropsHook: PropsHookFn<TProps, TState, TResultProps>,
|
||||
defaultRender: TRenderFn,
|
||||
displayName: string,
|
||||
) => {
|
||||
const ConnectedComponent = ({
|
||||
render = defaultRender,
|
||||
...props
|
||||
}: TProps & { render?: TRenderFn }) => {
|
||||
const connectedState = useStateHook(props as TProps);
|
||||
const connectedProps = usePropsHook(props as TProps, connectedState);
|
||||
return render(connectedProps, connectedState);
|
||||
};
|
||||
|
||||
ConnectedComponent.displayName = displayName;
|
||||
return ConnectedComponent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Type helper to infer the component type from the factory
|
||||
*/
|
||||
export type ConnectedComponent<
|
||||
TProps extends Record<string, any>,
|
||||
TRenderFn extends RenderFn<any, any>
|
||||
> = React.FC<TProps & { render?: TRenderFn }>;
|
||||
Reference in New Issue
Block a user