Files
metabuilder/hooks/RadioGroup.tsx
johndoe6345789 78a54228df feat(hooks): Create centralized hooks npm package
- Added @metabuilder/hooks workspace package at root
- Consolidated 30 React hooks from across codebase into single module
- Implemented conditional exports for tree-shaking support
- Added comprehensive package.json with build/lint/typecheck scripts
- Created README.md documenting hook categories and usage patterns
- Updated root package.json workspaces array to include hooks
- Supports multi-version peer dependencies (React 18/19, Redux 8/9)

Usage:
  import { useDashboardLogic } from '@metabuilder/hooks'
  import { useLoginLogic } from '@metabuilder/hooks/useLoginLogic'

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-23 19:17:17 +00:00

95 lines
2.6 KiB
TypeScript

import React, { forwardRef, createContext, useContext, Children, cloneElement, isValidElement, useId } from 'react'
/**
* RadioGroup context value
*/
interface RadioGroupContextValue {
name?: string
value?: string
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
}
const RadioGroupContext = createContext<RadioGroupContextValue>({})
/**
* Hook to access RadioGroup context
*/
export const useRadioGroup = () => useContext(RadioGroupContext)
/**
* Props for RadioGroup component
*/
export interface RadioGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
children?: React.ReactNode
/** Name attribute for all radio buttons */
name?: string
/** Currently selected value */
value?: string
/** Default value for uncontrolled usage */
defaultValue?: string
/** Called when selection changes */
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
/** Stack radio buttons horizontally */
row?: boolean
}
/**
* RadioGroup - Groups Radio buttons with shared name and selection state
*
* @example
* ```tsx
* <RadioGroup name="size" value={size} onChange={handleChange}>
* <Radio value="small" label="Small" />
* <Radio value="medium" label="Medium" />
* <Radio value="large" label="Large" />
* </RadioGroup>
* ```
*/
export const RadioGroup = forwardRef<HTMLDivElement, RadioGroupProps>(
(
{
children,
name: nameProp,
value,
defaultValue,
onChange,
row = false,
className = '',
...props
},
ref
) => {
const generatedName = useId()
const name = nameProp ?? generatedName
// Enhance child Radio components with group context
const enhancedChildren = Children.map(children, (child) => {
if (isValidElement(child)) {
const childValue = (child.props as Record<string, unknown>).value as string | undefined
return cloneElement(child as React.ReactElement<Record<string, unknown>>, {
name,
checked: value !== undefined ? childValue === value : undefined,
defaultChecked: defaultValue !== undefined ? childValue === defaultValue : undefined,
onChange,
})
}
return child
})
return (
<RadioGroupContext.Provider value={{ name, value, onChange }}>
<div
ref={ref}
role="radiogroup"
className={`radio-group ${row ? 'radio-group--row' : ''} ${className}`}
{...props}
>
{enhancedChildren}
</div>
</RadioGroupContext.Provider>
)
}
)
RadioGroup.displayName = 'RadioGroup'