diff --git a/packages/radio/src/components/Radio.tsx b/packages/radio/src/components/Radio.tsx index 363fc6a5..7be76f40 100644 --- a/packages/radio/src/components/Radio.tsx +++ b/packages/radio/src/components/Radio.tsx @@ -1,11 +1,11 @@ /** @jsxImportSource @emotion/react */ -import * as Style from '../styles'; import { useId, useContext, forwardRef } from 'react'; import { createClassVariant, getColorByTokenOrHex } from '@jdesignlab/theme'; import { getComponentText } from '@jdesignlab/react-utils'; -import { RadioContext } from '../context'; import RadioGroup from './RadioGroup'; import RadioLabel from './RadioLabel'; +import { RadioContext } from '../context'; +import * as Style from '../styles'; import useRadio from '../hooks/useRadio'; import { DEFAULT_COLOR, DEFAULT_DISABLED_COLOR, RADIO_ID_PREFIX } from '../constants'; import validateEventHandlers from '../utils/validateEventHandlers'; @@ -26,6 +26,7 @@ export const Radio = Object.assign( name = '', ...restProps } = props; + const uuid = useId(); const radioContext = useContext(RadioContext); const isUnavailable = !!readonly || !!disabled; const validRadioProps = validateEventHandlers(isUnavailable, restProps, radioContext?.rootProps); @@ -34,7 +35,7 @@ export const Radio = Object.assign( validRadioProps, radioContext?.setValue ); - const radioId = id ?? `${RADIO_ID_PREFIX}-${useId()}`; + const radioId = id ?? `${RADIO_ID_PREFIX}-${uuid}`; const radioClassName = className ? `${createClassVariant('radio', 'wrapper')} ${className}` : createClassVariant('radio', 'wrapper'); diff --git a/packages/radio/src/components/RadioGroup.tsx b/packages/radio/src/components/RadioGroup.tsx index b7677fb1..0dec3a24 100644 --- a/packages/radio/src/components/RadioGroup.tsx +++ b/packages/radio/src/components/RadioGroup.tsx @@ -6,7 +6,7 @@ import type { RadioGroupProps } from '../types'; const RadioGroup = Object.assign( forwardRef((props: RadioGroupProps, ref) => { - const { children, defaultValue = null, ...restProps } = props; + const { children, defaultValue, ...restProps } = props; const radioGroupRef = useRadioGroup(defaultValue, ref); return ( diff --git a/packages/radio/src/components/RadioLabel.tsx b/packages/radio/src/components/RadioLabel.tsx index df563539..95e424fd 100644 --- a/packages/radio/src/components/RadioLabel.tsx +++ b/packages/radio/src/components/RadioLabel.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @emotion/react */ -import * as Style from '../styles'; import { createClassVariant } from '@jdesignlab/theme'; +import * as Style from '../styles'; import { DEFAULT_FONT_COLOR } from '../constants'; import type { RadioLabelProps } from '../types'; diff --git a/packages/radio/src/components/RadioProvider.tsx b/packages/radio/src/components/RadioProvider.tsx index e061699d..b3b4851d 100644 --- a/packages/radio/src/components/RadioProvider.tsx +++ b/packages/radio/src/components/RadioProvider.tsx @@ -1,23 +1,31 @@ +import type { ReactNode } from 'react'; +import { useId, useMemo, useState } from 'react'; import { RadioContext } from '../context'; -import { useId, useState } from 'react'; import { RADIO_NAME_PREFIX } from '../constants'; +import type { RadioGroupProps, ReturnContext } from '../types'; -export const RadioProvider = ({ ...props }) => { - const { children, defaultValue = null, rootProps = null } = props; - const defaultName = rootProps && rootProps.name ? rootProps.name : `${RADIO_NAME_PREFIX}_${useId()}`; +interface RadioProviderProps { + defaultValue?: string; + children: ReactNode; + rootProps?: RadioGroupProps; +} + +export const RadioProvider = ({ ...props }: RadioProviderProps) => { + const { children, defaultValue, rootProps } = props; + const uuid = useId(); + const defaultName = `${RADIO_NAME_PREFIX}_${uuid}`; const [value, setValue] = useState(''); - return ( - - {children} - + const providerValue = useMemo( + () => ({ + setValue, + value, + defaultValue, + name: defaultName, + rootProps: { ...rootProps } + }), + [value, setValue, defaultValue, defaultName, rootProps] ); + + return {children}; }; diff --git a/packages/radio/src/hooks/useRadio.ts b/packages/radio/src/hooks/useRadio.ts index 48fc9cb8..f562b371 100644 --- a/packages/radio/src/hooks/useRadio.ts +++ b/packages/radio/src/hooks/useRadio.ts @@ -1,12 +1,12 @@ +import type { Dispatch, SetStateAction } from 'react'; import { ChangeEvent, useCallback, useRef, useState } from 'react'; -import { RadioAttributes } from '../types'; import { callHandler } from '@jdesignlab/utils'; +import { RadioProps } from '../types'; import type { EventType } from '@jdesignlab/utils'; -import type { Dispatch, SetStateAction } from 'react'; const useRadio = ( isUnavailable: boolean, - radioProps: RadioAttributes, + radioProps: RadioProps, contextSetValue?: Dispatch> | null ) => { const [radioValue, setValue] = useState(''); @@ -30,12 +30,12 @@ const useRadio = ( return propsOnKeyDown ? callHandler(defaultHandleKeydown, propsOnKeyDown) : defaultHandleKeydown; }, - [radioRef] + [contextSetValue, isUnavailable] ); const combineChangeHandler = (propsOnChange?: (event: EventType) => void) => { - const deafultHandleChange = (e: ChangeEvent) => { - const value = e.target.value; + const defaultHandleChange = (e: ChangeEvent) => { + const { value } = e.target; if (isUnavailable) { e.preventDefault(); return; @@ -48,7 +48,7 @@ const useRadio = ( setValue(value); }; - return propsOnChange ? callHandler(deafultHandleChange, propsOnChange) : deafultHandleChange; + return propsOnChange ? callHandler(defaultHandleChange, propsOnChange) : defaultHandleChange; }; const handleChange = combineChangeHandler(radioProps.onChange as (event: EventType) => void); diff --git a/packages/radio/src/hooks/useRadioGroup.ts b/packages/radio/src/hooks/useRadioGroup.ts index fd3e914e..9d3a0a9e 100644 --- a/packages/radio/src/hooks/useRadioGroup.ts +++ b/packages/radio/src/hooks/useRadioGroup.ts @@ -1,29 +1,30 @@ import { ForwardedRef, useCallback, useEffect, useRef } from 'react'; -const useRadioGroup = (defalutValue: string | null, externalRef?: ForwardedRef) => { +const useRadioGroup = (defaultValue?: string, externalRef?: ForwardedRef) => { const ref = useRef(null); const setDefaultValueCheck = useCallback( - (el: HTMLInputElement, targetValue: string | null, externalRef?: ForwardedRef) => { + (el: HTMLInputElement, targetValue?: string, extRef?: ForwardedRef) => { const isUnavailable = el.hasAttribute('readonly') || el.hasAttribute('disabled'); if (targetValue && targetValue === el.value && !isUnavailable) { - el.checked = true; + Object.assign(el, { checked: true }); } - if (externalRef && typeof externalRef === 'function') { - externalRef(el); + if (extRef && typeof extRef === 'function') { + extRef(el); } + return el; }, - [ref] + [] ); useEffect(() => { if (ref.current) { const radioElements = ref.current.querySelectorAll('input[type="radio"]') as NodeListOf; radioElements.forEach(el => { - setDefaultValueCheck(el, defalutValue, externalRef); + setDefaultValueCheck(el, defaultValue, externalRef); }); } - }, [ref]); + }, [defaultValue, externalRef, setDefaultValueCheck]); return ref; }; diff --git a/packages/radio/src/styles.ts b/packages/radio/src/styles.ts index 49f7aabc..8545cf7a 100644 --- a/packages/radio/src/styles.ts +++ b/packages/radio/src/styles.ts @@ -200,9 +200,8 @@ export const createRadio = (color: HEX, disabledColor: HEX, disabled: boolean, s return [radioStyle, disabledStyle(), radioSizeStyle()]; }; -export const wrapper = () => { - return css({ +export const wrapper = () => + css({ display: 'inline-block', position: 'relative' }); -}; diff --git a/packages/radio/src/types/base.ts b/packages/radio/src/types/base.ts index 94f1ef15..09a31386 100644 --- a/packages/radio/src/types/base.ts +++ b/packages/radio/src/types/base.ts @@ -1,2 +1,2 @@ export type RadioSize = 'sm' | 'md' | 'lg'; -export type RadioValueType = string | number | boolean; +export type RadioValueType = string | number | readonly string[] | undefined | boolean; diff --git a/packages/radio/src/types/context.ts b/packages/radio/src/types/context.ts index 12d12ca1..7bdea2f9 100644 --- a/packages/radio/src/types/context.ts +++ b/packages/radio/src/types/context.ts @@ -2,7 +2,7 @@ import type { Dispatch, SetStateAction } from 'react'; import type { EventType } from '@jdesignlab/utils'; import type { RadioValueType } from './base'; -export type RadioAttributes = { [key: string]: string | ((event: EventType) => void) }; +export type RadioAttributes = Record void)>; export interface ReturnContext { defaultValue?: string; name: string;